用于EF 4实体模型的DTO生成器

本文关键字:DTO EF 实体模型 用于 | 更新日期: 2023-09-27 18:01:26

是否有可能编写t4模板(或者如果它已经存在),它将能够基于*中的数据生成DTO类。edmx文件?

我必须为当前的项目编写DTO类,这个过程有点烦人。

我试图获得的是DTO类,它将标量属性定义为简单的自动属性,并将导航参数作为其他DTO类的封装实例。

的例子:

public class SomeClassDTO
{
    public byte Id { get; set; }
    public string Description { get; set; }        
    public OtherClassDTO SomeProperty {get;set;}
    public IList<AnotherClassDTO> Objects {get;set;}
}

这是一个很好的起点,更理想的可能是下面的示例:

/// <summary>
/// Employee details DTO.
/// </summary>
public class EmployeeDetailsDTO
{
    [Key] 
    public long Id { get; set; }
    [Required] 
    public string FirstName { get; set; }
    [Required]
    public string Surname { get; set; }
    ...
    public long? PhotoId { get; set; }
    // Home address properties.

    public string HomeAddressAddressLine1 { get; set; } // This is just name of field, not flattened list 
    public string HomeAddressAddressLine2 { get; set; }
    public string HomeAddressAddressLine3 { get; set; }
    public string HomeAddressPostcode { get; set; }
    public short? HomeAddressCountryId { get; set; }
    public long? HomeAddressCountyId { get; set; }
    public long? HomeAddressTownId { get; set; }

    public short? HomeTelephoneCountryId { get; set; }        
    public string HomeTelephoneNumber{ get; set; }
    public string HomeTelephoneExtension { get; set; }

    public short? PersonalMobileCountryId { get; set; }      
    public string PersonalMobileNumber { get; set; }
    public string PersonalMobileExtension { get; set; }
}

正如你所看到的,这是一个表示复合结构的扁平DTO,可以通过ValueInjector SameNameFlat/UnFlat注入注入回实体。

这是最终的目标,尽管任何建议都将是感激的

用于EF 4实体模型的DTO生成器

我最近在CodePlex上发布了一个名为EntitiesToDTOs的实体框架DTO生成器,它是免费和开源的,它被用作Visual Studio 2010和2012的插件。我想这会对你有帮助。

去http://entitiestodtos.codeplex.com下载,告诉我你的想法;)

我终于能够为c#语言创建一个项目模板,它将在VS 2010中为您创建DTO类。请点击链接:http://www.stepupframeworks.com/Home/products/entity-to-dto-creator/

请查看链接:http://www.insidelogic.nl/Blog/tabid/146/EntryId/4/Create-DTO-Data-transfer-objects-from-an-Entity-Framework-edmx-file.aspx也许它会对你有帮助。

看起来比我想象的容易多了。如果有人感兴趣,有一个递归扫描实体树的t4模板:

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
 output extension=".cs"#><#
// Copyright (c) Microsoft Corporation.  All rights reserved.
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"D:'Projects'Empresa'CTM_Empresa'trunk'CTM.Empresa.Logic'DataModel'CTM.Empresa.Database.edmx";
string entityName = @"Email";
bool printNavigationLists = false;
MetadataWorkspace metadataWorkspace = null;
bool allMetadataLoaded = loader.TryLoadAllMetadata(inputFile, out metadataWorkspace);
EdmItemCollection ItemCollection = (EdmItemCollection)metadataWorkspace.GetItemCollection(DataSpace.CSpace);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
RecursiveEntityProcessor.code = code;
RecursiveEntityProcessor.ItemCollection = ItemCollection;
RecursiveEntityProcessor.metaData = ef;
// Emit Entity Types
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().Where(it => it.Name == entityName))
{
    fileManager.StartNewFile(entity.Name + ".cs");
    var result = new List<PropertyCustomData>();    
    RecursiveEntityProcessor.GetEntityPropertyNames(result, entity.Name , 2 , "");  
    //WriteEntityTypeSerializationInfo(entity, ItemCollection, code, ef);
#>
<#=Accessibility.ForType(entity)#> <#=code.SpaceAfter(code.AbstractOption(entity))#>class <#=code.Escape(entity)#><#=code.StringBefore(" : ", code.Escape(entity.BaseType))#>
{
<# 
    foreach (PropertyCustomData property in result)
    {       
        #>
    /// <summary>
    /// Gets or sets <#=code.Escape(property.PropertyName)#>.
    /// </summary>
    <#=Accessibility.ForProperty(property.PropertyDefenition)#> <#=code.Escape(property.PropertyDefenition.TypeUsage)#> <#=code.Escape(property.PropertyName)#> { get; set; }
<#
    }    
}#> 
}
<#+ 
    public class PropertyCustomData
    {
        public EdmProperty PropertyDefenition { get; set; }
        public string PropertyName { get; set; }
    }
    public static class RecursiveEntityProcessor
    {       
        public static CodeGenerationTools code;
        public static EdmItemCollection ItemCollection;
        public static MetadataTools metaData;
        public static EntityType FindByName(string entityName)
        {
            return ItemCollection.GetItems<EntityType>().Single(it => it.Name == entityName);
        }
        public static void GetEntityPropertyNames(IList<PropertyCustomData> result, string entityName, int recursiveDepth, string currentPrefix, bool canPrintPrimaryKeys = true)
        {           
            if (recursiveDepth > 0 )
            {
                EntityType entity = FindByName(entityName);
                foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity))
                {
                    if (metaData.IsKey(edmProperty) && !canPrintPrimaryKeys)
                    {
                        continue;
                    }
                    result.Add(new PropertyCustomData { PropertyDefenition = edmProperty, PropertyName = currentPrefix + code.Escape(edmProperty) });
                }
                foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
                {
                    if (navProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)
                    {
                        string childEntityName = navProperty.ToEndMember.GetEntityType().Name;
                        GetEntityPropertyNames(result, childEntityName, recursiveDepth - 1, currentPrefix + code.Escape(navProperty), false);
                    }
                }
            }
        }
    }
#>

这个模板产生的代码类似于我的问题的最后一个例子。

这是Visual Studio 2012 T4模板的更新,可以在现有EDMX文件的基础上生成简单的DTO对象。它跳过导航属性,只生成简单的属性。

使用AutoMapper,我能够将POCO数据复制到DTO对象中。它们是可序列化的,可以作为XML传输。在目标站点上重建对象时,可以将它们附加到dbContext并调用DetectChanges()。参考文献将在以后被修正。

<#@ template language="C#" debug="true" hostspecific="true" #>
<#@ Assembly Name="System.Core, Version=4.0.0.0, Culture=neutral" #>
<#@ Assembly Name="Microsoft.CSharp, Version=4.0.0.0, Culture=neutral" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs"#>
<#

const string inputFile = @"../Model/ModelTest.edmx";
var textTransform = DynamicTextTransformation.Create(this);
var code = new CodeGenerationTools(this);
var ef = new MetadataTools(this);
var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
var fileManager = EntityFrameworkTemplateFileManager.Create(this);
var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);
if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile))
{
    return string.Empty;
}
WriteHeader(codeStringGenerator, fileManager);
foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
    fileManager.StartNewFile(entity.Name + "Dto.cs");
    BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(false)#>
<#=codeStringGenerator.EntityClassOpening(entity)#>
{
<#
    var simpleProperties = typeMapper.GetSimpleProperties(entity);
    if (simpleProperties.Any())
    {
        foreach (var edmProperty in simpleProperties)
        {
#>
    <#=codeStringGenerator.Property(edmProperty)#>
<#
        }
    }
#>
}
<#
    EndNamespace(code);
}
foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection))
{
    fileManager.StartNewFile(complex.Name + ".cs");
    BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(false, false)#>
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
{
<#
    var simpleProperties = typeMapper.GetSimpleProperties(complex);
    if (simpleProperties.Any())
    {
        foreach(var edmProperty in simpleProperties)
        {
#>
    <#=codeStringGenerator.Property(edmProperty)#>
<#
        }
    }
#>
}
<#
    EndNamespace(code);
}
fileManager.Process();
#>
<#+
public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager)
{
    fileManager.StartHeader();
#>
//------------------------------------------------------------------------------
// <auto-generated>
// <#=GetResourceString("Template_GeneratedCodeCommentLine1")#>
//
// <#=GetResourceString("Template_GeneratedCodeCommentLine2")#>
// <#=GetResourceString("Template_GeneratedCodeCommentLine3")#>
// </auto-generated>
//------------------------------------------------------------------------------
<#=codeStringGenerator.UsingDirectives(true)#>
<#+
    fileManager.EndBlock();
}
public void BeginNamespace(CodeGenerationTools code)
{
    var codeNamespace = code.VsNamespaceSuggestion();
    if (!String.IsNullOrEmpty(codeNamespace))
    {
#>
namespace <#=code.EscapeNamespace(codeNamespace)#>
{
<#+
        PushIndent("    ");
    }
}
public void EndNamespace(CodeGenerationTools code)
{
    if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion()))
    {
        PopIndent();
#>
}
<#+
    }
}
public const string TemplateId = "CSharp_DbContext_Types_EF5";
public class CodeStringGenerator
{
    private readonly CodeGenerationTools _code;
    private readonly TypeMapper _typeMapper;
    private readonly MetadataTools _ef;
    public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)
    {
        ArgumentNotNull(code, "code");
        ArgumentNotNull(typeMapper, "typeMapper");
        ArgumentNotNull(ef, "ef");
        _code = code;
        _typeMapper = typeMapper;
        _ef = ef;
    }
    public string Property(EdmProperty edmProperty)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
            "[DataMember()] {0} {1} {2} {3} {{get; {4}set; }}",
            Accessibility.ForProperty(edmProperty),
            _typeMapper.GetTypeName(edmProperty.TypeUsage),
            _code.Escape(edmProperty),
            _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
            _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
    }

    public string EntityClassOpening(EntityType entity)
    {
            return string.Format(
                CultureInfo.InvariantCulture,
                "[DataContract()]'r'n{0} {1}partial class {2}Dto{3}",
                Accessibility.ForType(entity),
                _code.SpaceAfter(_code.AbstractOption(entity)),
                _code.Escape(entity),
                _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
    }

    public string UsingDirectives(bool inHeader, bool includeCollections = true)
    {
        return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
            ? string.Format(
                CultureInfo.InvariantCulture,
                "{0}using System;" +Environment.NewLine+
                "using System.Runtime.Serialization;{1}" +
                "{2}",
                inHeader ? Environment.NewLine : "",
                includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
                inHeader ? "" : Environment.NewLine)
            : "";
    }
}
public class TypeMapper
{
    private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName";
    private readonly System.Collections.IList _errors;
    private readonly CodeGenerationTools _code;
    private readonly MetadataTools _ef;
    public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)
    {
        ArgumentNotNull(code, "code");
        ArgumentNotNull(ef, "ef");
        ArgumentNotNull(errors, "errors");
        _code = code;
        _ef = ef;
        _errors = errors;
    }
    public string GetTypeName(TypeUsage typeUsage)
    {
        return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), null);
    }
    public string GetTypeName(EdmType edmType)
    {
        return GetTypeName(edmType, null,  null);
    }
    public string GetTypeName(TypeUsage typeUsage, string modelNamespace)
    {
        return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);
    }
    public string GetTypeName(EdmType edmType, string modelNamespace)
    {
        return GetTypeName(edmType, null, modelNamespace);
    }
    public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)
    {
        if (edmType == null)
        {
            return null;
        }
        var collectionType = edmType as CollectionType;
        if (collectionType != null)
        {
            return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace));
        }
        var typeName = _code.Escape(edmType.MetadataProperties
                                .Where(p => p.Name == ExternalTypeNameAttributeName)
                                .Select(p => (string)p.Value)
                                .FirstOrDefault())
            ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?
                _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :
                _code.Escape(edmType));
        if (edmType is StructuralType)
        {
            return typeName;
        }
        if (edmType is SimpleType)
        {
            var clrType = UnderlyingClrType(edmType);
            if (!IsEnumType(edmType))
            {
                typeName = _code.Escape(clrType);
            }
            return clrType.IsValueType && isNullable == true ?
                String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) :
                typeName;
        }
        throw new ArgumentException("edmType");
    }
    public Type UnderlyingClrType(EdmType edmType)
    {
        ArgumentNotNull(edmType, "edmType");
        var primitiveType = edmType as PrimitiveType;
        if (primitiveType != null)
        {
            return primitiveType.ClrEquivalentType;
        }
        if (IsEnumType(edmType))
        {
            return GetEnumUnderlyingType(edmType).ClrEquivalentType;
        }
        return typeof(object);
    }

    public bool IsEnumType(GlobalItem edmType)
    {
        ArgumentNotNull(edmType, "edmType");
        return edmType.GetType().Name == "EnumType";
    }
    public PrimitiveType GetEnumUnderlyingType(EdmType enumType)
    {
        ArgumentNotNull(enumType, "enumType");
        return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null);
    }
    public string CreateLiteral(object value)
    {
        if (value == null || value.GetType() != typeof(TimeSpan))
        {
            return _code.CreateLiteral(value);
        }
        return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks);
    }
    public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)
    {
        ArgumentNotNull(types, "types");
        ArgumentNotNull(sourceFile, "sourceFile");
        var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
        if (types.Any(item => !hash.Add(item)))
        {
            _errors.Add(
                new CompilerError(sourceFile, -1, -1, "6023",
                    String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict"))));
            return false;
        }
        return true;
    }

    public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType
    {
        return itemCollection
            .OfType<T>()
            .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))
            .OrderBy(i => i.Name);
    }
    public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)
    {
        return itemCollection
            .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))
            .Select(g => GetGlobalItemName(g));
    }
    public string GetGlobalItemName(GlobalItem item)
    {
        if (item is EdmType)
        {
            return ((EdmType)item).Name;
        }
        else
        {
            return ((EntityContainer)item).Name;
        }
    }
    public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)
    {
        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
    }
    public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)
    {
        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
    }
}
public class EdmMetadataLoader
{
    private readonly IDynamicHost _host;
    private readonly System.Collections.IList _errors;
    public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors)
    {
        ArgumentNotNull(host, "host");
        ArgumentNotNull(errors, "errors");
        _host = host;
        _errors = errors;
    }
    public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath)
    {
        ArgumentNotNull(sourcePath, "sourcePath");
        if (!ValidateInputPath(sourcePath))
        {
            return new EdmItemCollection();
        }
        var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath));
        if (schemaElement != null)
        {
            using (var reader = schemaElement.CreateReader())
            {
                IList<EdmSchemaError> errors;
                var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors);
                ProcessErrors(errors, sourcePath);
                return itemCollection;
            }
        }
        return new EdmItemCollection();
    }
    public string GetModelNamespace(string sourcePath)
    {
        ArgumentNotNull(sourcePath, "sourcePath");
        if (!ValidateInputPath(sourcePath))
        {
            return string.Empty;
        }
        var model = LoadRootElement(_host.ResolvePath(sourcePath));
        if (model == null)
        {
            return string.Empty;
        }
        var attribute = model.Attribute("Namespace");
        return attribute != null ? attribute.Value : "";
    }
    private bool ValidateInputPath(string sourcePath)
    {
        if (sourcePath == "$" + "edmxInputFile" + "$")
        {
            _errors.Add(
                new CompilerError(_host.TemplateFile ?? sourcePath, 0, 0, string.Empty,
                    GetResourceString("Template_ReplaceVsItemTemplateToken")));
            return false;
        }
        return true;
    }
    public XElement LoadRootElement(string sourcePath)
    {
        ArgumentNotNull(sourcePath, "sourcePath");
        var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
        return root.Elements()
            .Where(e => e.Name.LocalName == "Runtime")
            .Elements()
            .Where(e => e.Name.LocalName == "ConceptualModels")
            .Elements()
            .Where(e => e.Name.LocalName == "Schema")
            .FirstOrDefault()
                ?? root;
    }
    private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath)
    {
        foreach (var error in errors)
        {
            _errors.Add(
                new CompilerError(
                    error.SchemaLocation ?? sourceFilePath,
                    error.Line,
                    error.Column,
                    error.ErrorCode.ToString(CultureInfo.InvariantCulture),
                    error.Message)
                {
                    IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning
                });
        }
    }

}
public static void ArgumentNotNull<T>(T arg, string name) where T : class
{
    if (arg == null)
    {
        throw new ArgumentNullException(name);
    }
}
private static readonly Lazy<System.Resources.ResourceManager> ResourceManager =
    new Lazy<System.Resources.ResourceManager>(
        () => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly),  true);
public static string GetResourceString(string resourceName)
{
    ArgumentNotNull(resourceName, "resourceName");
    return ResourceManager.Value.GetString(resourceName, null);
}
#>