【问题标题】:DTO Generator for EF 4 Entity modelEF 4 实体模型的 DTO 生成器
【发布时间】:2011-09-29 16:30:51
【问题描述】:

是否可以编写能够根据 *.edmx 文件中的数据生成 DTO 类的 t4 模板(或者如果它已经存在)?

我要为当前项目写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 注入将其注入实体。

这是最终目标,但我们将不胜感激。

【问题讨论】:

  • DTO 是用于单一目的的专用对象,在大多数情况下,它的创建不能自动化,因为它通常包含其他属性或缺少实体的某些属性(它不是实体的 1:1 映射)所以创建只是实体副本的 DTO 看起来更像是架构中的问题。甚至您的递归 T4 模板也可以添加您不需要的属性。 DTO 应该只传递使用 DTO 的操作所需的属性。
  • 是的,当然,这个 t4 的东西只是让它变得更容易。至少我不必为我创建的每个 dto 手动编写骨架。当我被扫描课时,我可以将它切成我想要的部分。

标签: c# visual-studio code-generation t4 dto


【解决方案1】:

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

http://entitiestodtos.codeplex.com下载它,让我知道你的想法;)

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      【讨论】:

      【解决方案4】:

      这似乎比我想象的要容易得多。如果有人有兴趣,有一个 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);
                          }
                      }
                  }
              }
          }
      
      #>
      

      此模板生成的代码类似于我的问题的最后一个示例。

      【讨论】:

        【解决方案5】:

        这是 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);
        }
        
        #>
        

        【讨论】:

          猜你喜欢
          • 2012-03-24
          • 1970-01-01
          • 2015-03-06
          • 1970-01-01
          • 2011-07-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-28
          相关资源
          最近更新 更多