• 框架计划随笔 三.EntityFramework在传统事务脚本模式下的使用


      某个朋友问为什么不推首页或者允许评论,我说一直没怎么写博客,也习惯了先随便乱画再开始写文档,担心公开后一些不经意的"呓语“中得出的错误的结论会给别人错误的观点,所以这个系列只是当做熟悉写博客的习作和研究笔记。等过后有时间了再修改完善或者另开一个更系统的专题。

      EF出来不短的时间了,为什么一直没用?因为其实有很长一段时间没有负责架构搭建的工作了,更多的时间花在前端,数据库,文档,客户需求方面。当然精力不足只是借口,更多的是形成了一种不好的技术惰性,总认为能理解是怎样的就行了,如果没有项目需求,没必要去了解技术细节。

      使用之前做过一些相关资料的搜集,了解到EF其实是非常棒的一个框架组件。以前开发的项目,绝大部分是先从数据库设计开始,用工具画实体图或者数据库设计图,生成数据库,修改好后,用自己编写的代码生成工具或者脚本工具,生成相应ORM框架的基础代码。用这种方式其实有不少问题:开发中后期数据库结构已经和最初的设计有很大的不同,即使开始想得再完美(很多时候并没有那么多时间去想完美),中间文档再完善,后期进入的开发人员,想要通过设计图去理解数据结构,都会变得很难,这其实也是和项目控制有关,绝大多数公司都没有DBA,对项目开发过程中的数据库更改没有进行太严格的审核。另外,设计和实际的代码编辑的分离,即使将设计文档和项目放在一起做源代码管理,做控制的时候也不方便,毕竟一个是文档,一个是代码,再加上数据库,三者在项目过程中是紧密结合的,但是在修改上又是相互独立的。比如新需求需要对数据库结构进行调整,大多数情况下我需要先对设计图进行修改,然后手动修改数据库结构,再修改相应的代码实现。整个过程是脱离的,如果一个公司或者项目组有完善的管理机制,这并不是太大的问题,相应的人员管理相应的步奏就行,但是实际开发中,即使再严格按照规范来管理,还是会出现类似的情况,因为开发过程还是控制在开发人员手中,有时候为了调试方便,直接修改数据库而不做更改记录的情况屡见不鲜。EF其实在某种程度上缓和了这种涉及需求更改的问题,无论是codefirst还是dbfirst,更改过程都可以直观的展示在模板中,而通过对模板的源代码管理,可以很直观的看到数据库或者数据实体设计上的变更。

      我们还是转到实际使用上来,怎么使用dbfirst或者codefirst,园子里面已经有很多的文章,我这里就不一步一步展示了,说一下我的测试项目中几个小小的改造

      1.T4脚本分离

      使用EF自带的模板文件,生成代码的T4模板其实有点太臃肿了,代码不复杂,但是就是因为将生成代码和基础函数以及模板代码放在一起,让很多人觉得看起来很麻烦。这是自动生成的T4模板。

      1 <#@ template language="C#" debug="false" hostspecific="true"#>
      2 <#@ include file="EF.Utility.CS.ttinclude"#><#@ 
      3  output extension=".cs"#><#
      4 
      5 const string inputFile = @"SqlServer.edmx";
      6 var textTransform = DynamicTextTransformation.Create(this);
      7 var code = new CodeGenerationTools(this);
      8 var ef = new MetadataTools(this);
      9 var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
     10 var    fileManager = EntityFrameworkTemplateFileManager.Create(this);
     11 var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
     12 var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);
     13 
     14 if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile))
     15 {
     16     return string.Empty;
     17 }
     18 
     19 WriteHeader(codeStringGenerator, fileManager);
     20 
     21 foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
     22 {
     23     fileManager.StartNewFile(entity.Name + ".cs");
     24     BeginNamespace(code);
     25 #>
     26 <#=codeStringGenerator.UsingDirectives(inHeader: false)#>
     27 <#=codeStringGenerator.EntityClassOpening(entity)#>
     28 {
     29 <#
     30     var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);
     31     var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);
     32     var complexProperties = typeMapper.GetComplexProperties(entity);
     33 
     34     if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())
     35     {
     36 #>
     37     public <#=code.Escape(entity)#>()
     38     {
     39 <#
     40         foreach (var edmProperty in propertiesWithDefaultValues)
     41         {
     42 #>
     43         this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
     44 <#
     45         }
     46 
     47         foreach (var navigationProperty in collectionNavigationProperties)
     48         {
     49 #>
     50         this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
     51 <#
     52         }
     53 
     54         foreach (var complexProperty in complexProperties)
     55         {
     56 #>
     57         this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
     58 <#
     59         }
     60 #>
     61     }
     62 
     63 <#
     64     }
     65 
     66     var simpleProperties = typeMapper.GetSimpleProperties(entity);
     67     if (simpleProperties.Any())
     68     {
     69         foreach (var edmProperty in simpleProperties)
     70         {
     71 #>
     72     <#=codeStringGenerator.Property(edmProperty)#>
     73 <#
     74         }
     75     }
     76 
     77     if (complexProperties.Any())
     78     {
     79 #>
     80 
     81 <#
     82         foreach(var complexProperty in complexProperties)
     83         {
     84 #>
     85     <#=codeStringGenerator.Property(complexProperty)#>
     86 <#
     87         }
     88     }
     89 
     90     var navigationProperties = typeMapper.GetNavigationProperties(entity);
     91     if (navigationProperties.Any())
     92     {
     93 #>
     94 
     95 <#
     96         foreach (var navigationProperty in navigationProperties)
     97         {
     98 #>
     99     <#=codeStringGenerator.NavigationProperty(navigationProperty)#>
    100 <#
    101         }
    102     }
    103 #>
    104 }
    105 <#
    106     EndNamespace(code);
    107 }
    108 
    109 foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection))
    110 {
    111     fileManager.StartNewFile(complex.Name + ".cs");
    112     BeginNamespace(code);
    113 #>
    114 <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
    115 <#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
    116 {
    117 <#
    118     var complexProperties = typeMapper.GetComplexProperties(complex);
    119     var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex);
    120 
    121     if (propertiesWithDefaultValues.Any() || complexProperties.Any())
    122     {
    123 #>
    124     public <#=code.Escape(complex)#>()
    125     {
    126 <#
    127         foreach (var edmProperty in propertiesWithDefaultValues)
    128         {
    129 #>
    130         this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
    131 <#
    132         }
    133 
    134         foreach (var complexProperty in complexProperties)
    135         {
    136 #>
    137         this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
    138 <#
    139         }
    140 #>
    141     }
    142 
    143 <#
    144     }
    145 
    146     var simpleProperties = typeMapper.GetSimpleProperties(complex);
    147     if (simpleProperties.Any())
    148     {
    149         foreach(var edmProperty in simpleProperties)
    150         {
    151 #>
    152     <#=codeStringGenerator.Property(edmProperty)#>
    153 <#
    154         }
    155     }
    156 
    157     if (complexProperties.Any())
    158     {
    159 #>
    160 
    161 <#
    162         foreach(var edmProperty in complexProperties)
    163         {
    164 #>
    165     <#=codeStringGenerator.Property(edmProperty)#>
    166 <#
    167         }
    168     }
    169 #>
    170 }
    171 <#
    172     EndNamespace(code);
    173 }
    174 
    175 foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection))
    176 {
    177     fileManager.StartNewFile(enumType.Name + ".cs");
    178     BeginNamespace(code);
    179 #>
    180 <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
    181 <#
    182     if (typeMapper.EnumIsFlags(enumType))
    183     {
    184 #>
    185 [Flags]
    186 <#
    187     }
    188 #>
    189 <#=codeStringGenerator.EnumOpening(enumType)#>
    190 {
    191 <#
    192     var foundOne = false;
    193     
    194     foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType))
    195     {
    196         foundOne = true;
    197 #>
    198     <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>,
    199 <#
    200     }
    201 
    202     if (foundOne)
    203     {
    204         this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1);
    205     }
    206 #>
    207 }
    208 <#
    209     EndNamespace(code);
    210 }
    211 
    212 fileManager.Process();
    213 
    214 #>
    215 <#+
    216 
    217 public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager)
    218 {
    219     fileManager.StartHeader();
    220 #>
    221 //------------------------------------------------------------------------------
    222 // <auto-generated>
    223 // <#=GetResourceString("Template_GeneratedCodeCommentLine1")#>
    224 //
    225 // <#=GetResourceString("Template_GeneratedCodeCommentLine2")#>
    226 // <#=GetResourceString("Template_GeneratedCodeCommentLine3")#>
    227 // </auto-generated>
    228 //------------------------------------------------------------------------------
    229 <#=codeStringGenerator.UsingDirectives(inHeader: true)#>
    230 <#+
    231     fileManager.EndBlock();
    232 }
    233 
    234 public void BeginNamespace(CodeGenerationTools code)
    235 {
    236     var codeNamespace = code.VsNamespaceSuggestion();
    237     if (!String.IsNullOrEmpty(codeNamespace))
    238     {
    239 #>
    240 namespace <#=code.EscapeNamespace(codeNamespace)#>
    241 {
    242 <#+
    243         PushIndent("    ");
    244     }
    245 }
    246 
    247 public void EndNamespace(CodeGenerationTools code)
    248 {
    249     if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion()))
    250     {
    251         PopIndent();
    252 #>
    253 }
    254 <#+
    255     }
    256 }
    257 
    258 public const string TemplateId = "CSharp_DbContext_Types_EF5";
    259 
    260 public class CodeStringGenerator
    261 {
    262     private readonly CodeGenerationTools _code;
    263     private readonly TypeMapper _typeMapper;
    264     private readonly MetadataTools _ef;
    265 
    266     public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)
    267     {
    268         ArgumentNotNull(code, "code");
    269         ArgumentNotNull(typeMapper, "typeMapper");
    270         ArgumentNotNull(ef, "ef");
    271 
    272         _code = code;
    273         _typeMapper = typeMapper;
    274         _ef = ef;
    275     }
    276 
    277     public string Property(EdmProperty edmProperty)
    278     {
    279         return string.Format(
    280             CultureInfo.InvariantCulture,
    281             "{0} {1} {2} {{ {3}get; {4}set; }}",
    282             Accessibility.ForProperty(edmProperty),
    283             _typeMapper.GetTypeName(edmProperty.TypeUsage),
    284             _code.Escape(edmProperty),
    285             _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
    286             _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
    287     }
    288 
    289     public string NavigationProperty(NavigationProperty navigationProperty)
    290     {
    291         var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());
    292         return string.Format(
    293             CultureInfo.InvariantCulture,
    294             "{0} {1} {2} {{ {3}get; {4}set; }}",
    295             AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
    296             navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
    297             _code.Escape(navigationProperty),
    298             _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
    299             _code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
    300     }
    301     
    302     public string AccessibilityAndVirtual(string accessibility)
    303     {
    304         return accessibility + (accessibility != "private" ? " virtual" : "");
    305     }
    306     
    307     public string EntityClassOpening(EntityType entity)
    308     {
    309         return string.Format(
    310             CultureInfo.InvariantCulture,
    311             "{0} {1}partial class {2}{3}",
    312             Accessibility.ForType(entity),
    313             _code.SpaceAfter(_code.AbstractOption(entity)),
    314             _code.Escape(entity),
    315             _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
    316     }
    317     
    318     public string EnumOpening(SimpleType enumType)
    319     {
    320         return string.Format(
    321             CultureInfo.InvariantCulture,
    322             "{0} enum {1} : {2}",
    323             Accessibility.ForType(enumType),
    324             _code.Escape(enumType),
    325             _code.Escape(_typeMapper.UnderlyingClrType(enumType)));
    326         }
    327     
    328     public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)
    329     {
    330         var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
    331         foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))
    332         {
    333             var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null";
    334             var notNullInit = "new ObjectParameter("" + parameter.EsqlParameterName + "", " + parameter.FunctionParameterName + ")";
    335             var nullInit = "new ObjectParameter("" + parameter.EsqlParameterName + "", typeof(" + parameter.RawClrTypeName + "))";
    336             writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);
    337         }
    338     }
    339     
    340     public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)
    341     {
    342         var parameters = _typeMapper.GetParameters(edmFunction);
    343         
    344         return string.Format(
    345             CultureInfo.InvariantCulture,
    346             "{0} IQueryable<{1}> {2}({3})",
    347             AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
    348             _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
    349             _code.Escape(edmFunction),
    350             string.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()));
    351     }
    352     
    353     public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)
    354     {
    355         var parameters = _typeMapper.GetParameters(edmFunction);
    356         
    357         return string.Format(
    358             CultureInfo.InvariantCulture,
    359             "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>("[{1}].[{2}]({3})"{4});",
    360             _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
    361             edmFunction.NamespaceName,
    362             edmFunction.Name,
    363             string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()),
    364             _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())));
    365     }
    366     
    367     public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
    368     {
    369         var parameters = _typeMapper.GetParameters(edmFunction);
    370         var returnType = _typeMapper.GetReturnType(edmFunction);
    371 
    372         var paramList = String.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray());
    373         if (includeMergeOption)
    374         {
    375             paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption";
    376         }
    377 
    378         return string.Format(
    379             CultureInfo.InvariantCulture,
    380             "{0} {1} {2}({3})",
    381             AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
    382             returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
    383             _code.Escape(edmFunction),
    384             paramList);
    385     }
    386     
    387     public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
    388     {
    389         var parameters = _typeMapper.GetParameters(edmFunction);
    390         var returnType = _typeMapper.GetReturnType(edmFunction);
    391 
    392         var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
    393         if (includeMergeOption)
    394         {
    395             callParams = ", mergeOption" + callParams;
    396         }
    397         
    398         return string.Format(
    399             CultureInfo.InvariantCulture,
    400             "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}("{1}"{2});",
    401             returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
    402             edmFunction.Name,
    403             callParams);
    404     }
    405     
    406     public string DbSet(EntitySet entitySet)
    407     {
    408         return string.Format(
    409             CultureInfo.InvariantCulture,
    410             "{0} DbSet<{1}> {2} {{ get; set; }}",
    411             Accessibility.ForReadOnlyProperty(entitySet),
    412             _typeMapper.GetTypeName(entitySet.ElementType),
    413             _code.Escape(entitySet));
    414     }
    415 
    416     public string UsingDirectives(bool inHeader, bool includeCollections = true)
    417     {
    418         return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
    419             ? string.Format(
    420                 CultureInfo.InvariantCulture,
    421                 "{0}using System;{1}" +
    422                 "{2}",
    423                 inHeader ? Environment.NewLine : "",
    424                 includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
    425                 inHeader ? "" : Environment.NewLine)
    426             : "";
    427     }
    428 }
    429 
    430 public class TypeMapper
    431 {
    432     private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName";
    433 
    434     private readonly System.Collections.IList _errors;
    435     private readonly CodeGenerationTools _code;
    436     private readonly MetadataTools _ef;
    437 
    438     public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)
    439     {
    440         ArgumentNotNull(code, "code");
    441         ArgumentNotNull(ef, "ef");
    442         ArgumentNotNull(errors, "errors");
    443 
    444         _code = code;
    445         _ef = ef;
    446         _errors = errors;
    447     }
    448 
    449     public string GetTypeName(TypeUsage typeUsage)
    450     {
    451         return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);
    452     }
    453 
    454     public string GetTypeName(EdmType edmType)
    455     {
    456         return GetTypeName(edmType, isNullable: null, modelNamespace: null);
    457     }
    458 
    459     public string GetTypeName(TypeUsage typeUsage, string modelNamespace)
    460     {
    461         return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);
    462     }
    463 
    464     public string GetTypeName(EdmType edmType, string modelNamespace)
    465     {
    466         return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);
    467     }
    468 
    469     public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)
    470     {
    471         if (edmType == null)
    472         {
    473             return null;
    474         }
    475 
    476         var collectionType = edmType as CollectionType;
    477         if (collectionType != null)
    478         {
    479             return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace));
    480         }
    481 
    482         var typeName = _code.Escape(edmType.MetadataProperties
    483                                 .Where(p => p.Name == ExternalTypeNameAttributeName)
    484                                 .Select(p => (string)p.Value)
    485                                 .FirstOrDefault())
    486             ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?
    487                 _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :
    488                 _code.Escape(edmType));
    489 
    490         if (edmType is StructuralType)
    491         {
    492             return typeName;
    493         }
    494 
    495         if (edmType is SimpleType)
    496         {
    497             var clrType = UnderlyingClrType(edmType);
    498             if (!IsEnumType(edmType))
    499             {
    500                 typeName = _code.Escape(clrType);
    501             }
    502 
    503             return clrType.IsValueType && isNullable == true ?
    504                 String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) :
    505                 typeName;
    506         }
    507 
    508         throw new ArgumentException("edmType");
    509     }
    510     
    511     public Type UnderlyingClrType(EdmType edmType)
    512     {
    513         ArgumentNotNull(edmType, "edmType");
    514 
    515         var primitiveType = edmType as PrimitiveType;
    516         if (primitiveType != null)
    517         {
    518             return primitiveType.ClrEquivalentType;
    519         }
    520 
    521         if (IsEnumType(edmType))
    522         {
    523             return GetEnumUnderlyingType(edmType).ClrEquivalentType;
    524         }
    525 
    526         return typeof(object);
    527     }
    528     
    529     public object GetEnumMemberValue(MetadataItem enumMember)
    530     {
    531         ArgumentNotNull(enumMember, "enumMember");
    532         
    533         var valueProperty = enumMember.GetType().GetProperty("Value");
    534         return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);
    535     }
    536     
    537     public string GetEnumMemberName(MetadataItem enumMember)
    538     {
    539         ArgumentNotNull(enumMember, "enumMember");
    540         
    541         var nameProperty = enumMember.GetType().GetProperty("Name");
    542         return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);
    543     }
    544 
    545     public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)
    546     {
    547         ArgumentNotNull(enumType, "enumType");
    548 
    549         var membersProperty = enumType.GetType().GetProperty("Members");
    550         return membersProperty != null 
    551             ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)
    552             : Enumerable.Empty<MetadataItem>();
    553     }
    554     
    555     public bool EnumIsFlags(EdmType enumType)
    556     {
    557         ArgumentNotNull(enumType, "enumType");
    558         
    559         var isFlagsProperty = enumType.GetType().GetProperty("IsFlags");
    560         return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);
    561     }
    562 
    563     public bool IsEnumType(GlobalItem edmType)
    564     {
    565         ArgumentNotNull(edmType, "edmType");
    566 
    567         return edmType.GetType().Name == "EnumType";
    568     }
    569 
    570     public PrimitiveType GetEnumUnderlyingType(EdmType enumType)
    571     {
    572         ArgumentNotNull(enumType, "enumType");
    573 
    574         return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null);
    575     }
    576 
    577     public string CreateLiteral(object value)
    578     {
    579         if (value == null || value.GetType() != typeof(TimeSpan))
    580         {
    581             return _code.CreateLiteral(value);
    582         }
    583 
    584         return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks);
    585     }
    586     
    587     public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)
    588     {
    589         ArgumentNotNull(types, "types");
    590         ArgumentNotNull(sourceFile, "sourceFile");
    591         
    592         var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
    593         if (types.Any(item => !hash.Add(item)))
    594         {
    595             _errors.Add(
    596                 new CompilerError(sourceFile, -1, -1, "6023",
    597                     String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict"))));
    598             return false;
    599         }
    600         return true;
    601     }
    602     
    603     public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)
    604     {
    605         return GetItemsToGenerate<SimpleType>(itemCollection)
    606             .Where(e => IsEnumType(e));
    607     }
    608     
    609     public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType
    610     {
    611         return itemCollection
    612             .OfType<T>()
    613             .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))
    614             .OrderBy(i => i.Name);
    615     }
    616 
    617     public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)
    618     {
    619         return itemCollection
    620             .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))
    621             .Select(g => GetGlobalItemName(g));
    622     }
    623 
    624     public string GetGlobalItemName(GlobalItem item)
    625     {
    626         if (item is EdmType)
    627         {
    628             return ((EdmType)item).Name;
    629         }
    630         else
    631         {
    632             return ((EntityContainer)item).Name;
    633         }
    634     }
    635 
    636     public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)
    637     {
    638         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
    639     }
    640     
    641     public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)
    642     {
    643         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
    644     }
    645     
    646     public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)
    647     {
    648         return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
    649     }
    650     
    651     public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)
    652     {
    653         return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
    654     }
    655 
    656     public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)
    657     {
    658         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
    659     }
    660     
    661     public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)
    662     {
    663         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
    664     }
    665 
    666     public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)
    667     {
    668         return type.NavigationProperties.Where(np => np.DeclaringType == type);
    669     }
    670     
    671     public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)
    672     {
    673         return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
    674     }
    675     
    676     public FunctionParameter GetReturnParameter(EdmFunction edmFunction)
    677     {
    678         ArgumentNotNull(edmFunction, "edmFunction");
    679 
    680         var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters");
    681         return returnParamsProperty == null
    682             ? edmFunction.ReturnParameter
    683             : ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();
    684     }
    685 
    686     public bool IsComposable(EdmFunction edmFunction)
    687     {
    688         ArgumentNotNull(edmFunction, "edmFunction");
    689 
    690         var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute");
    691         return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);
    692     }
    693 
    694     public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)
    695     {
    696         return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
    697     }
    698 
    699     public TypeUsage GetReturnType(EdmFunction edmFunction)
    700     {
    701         var returnParam = GetReturnParameter(edmFunction);
    702         return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);
    703     }
    704     
    705     public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)
    706     {
    707         var returnType = GetReturnType(edmFunction);
    708         return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;
    709     }
    710 }
    711 
    712 public class EdmMetadataLoader
    713 {
    714     private readonly IDynamicHost _host;
    715     private readonly System.Collections.IList _errors;
    716 
    717     public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors)
    718     {
    719         ArgumentNotNull(host, "host");
    720         ArgumentNotNull(errors, "errors");
    721 
    722         _host = host;
    723         _errors = errors;
    724     }
    725 
    726     public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath)
    727     {
    728         ArgumentNotNull(sourcePath, "sourcePath");
    729 
    730         if (!ValidateInputPath(sourcePath))
    731         {
    732             return new EdmItemCollection();
    733         }
    734 
    735         var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath));
    736         if (schemaElement != null)
    737         {
    738             using (var reader = schemaElement.CreateReader())
    739             {
    740                 IList<EdmSchemaError> errors;
    741                 var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors);
    742 
    743                 ProcessErrors(errors, sourcePath);
    744 
    745                 return itemCollection;
    746             }
    747         }
    748         return new EdmItemCollection();
    749     }
    750 
    751     public string GetModelNamespace(string sourcePath)
    752     {
    753         ArgumentNotNull(sourcePath, "sourcePath");
    754 
    755         if (!ValidateInputPath(sourcePath))
    756         {
    757             return string.Empty;
    758         }
    759 
    760         var model = LoadRootElement(_host.ResolvePath(sourcePath));
    761         if (model == null)
    762         {
    763             return string.Empty;
    764         }
    765 
    766         var attribute = model.Attribute("Namespace");
    767         return attribute != null ? attribute.Value : "";
    768     }
    769 
    770     private bool ValidateInputPath(string sourcePath)
    771     {
    772         if (sourcePath == "$" + "edmxInputFile" + "$")
    773         {
    774             _errors.Add(
    775                 new CompilerError(_host.TemplateFile ?? sourcePath, 0, 0, string.Empty,
    776                     GetResourceString("Template_ReplaceVsItemTemplateToken")));
    777             return false;
    778         }
    779 
    780         return true;
    781     }
    782 
    783     public XElement LoadRootElement(string sourcePath)
    784     {
    785         ArgumentNotNull(sourcePath, "sourcePath");
    786 
    787         var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
    788         return root.Elements()
    789             .Where(e => e.Name.LocalName == "Runtime")
    790             .Elements()
    791             .Where(e => e.Name.LocalName == "ConceptualModels")
    792             .Elements()
    793             .Where(e => e.Name.LocalName == "Schema")
    794             .FirstOrDefault()
    795                 ?? root;
    796     }
    797 
    798     private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath)
    799     {
    800         foreach (var error in errors)
    801         {
    802             _errors.Add(
    803                 new CompilerError(
    804                     error.SchemaLocation ?? sourceFilePath,
    805                     error.Line,
    806                     error.Column,
    807                     error.ErrorCode.ToString(CultureInfo.InvariantCulture),
    808                     error.Message)
    809                 {
    810                     IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning
    811                 });
    812         }
    813     }
    814     
    815     public bool IsLazyLoadingEnabled(EntityContainer container)
    816     {
    817         string lazyLoadingAttributeValue;
    818         var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled";
    819         bool isLazyLoading;
    820         return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, out lazyLoadingAttributeValue)
    821             || !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading)
    822             || isLazyLoading;
    823     }
    824 }
    825 
    826 public static void ArgumentNotNull<T>(T arg, string name) where T : class
    827 {
    828     if (arg == null)
    829     {
    830         throw new ArgumentNullException(name);
    831     }
    832 }
    833     
    834 private static readonly Lazy<System.Resources.ResourceManager> ResourceManager =
    835     new Lazy<System.Resources.ResourceManager>(
    836         () => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true);
    837 
    838 public static string GetResourceString(string resourceName)
    839 {
    840     ArgumentNotNull(resourceName, "resourceName");
    841 
    842     return ResourceManager.Value.GetString(resourceName, null);
    843 }
    844 
    845 #>
    默认代码

      我进行了小小的改造,当然最终代码不是这样的,我先从原有的代码上进行分离,基本能满足需求

      1 <#@ template language="C#" debug="false" hostspecific="true"#>
      2 <#@ include file="EF.Utility.CS.ttinclude"#>
      3 <#+
      4 public class CodeStringGenerator
      5 {
      6     private readonly CodeGenerationTools _code;
      7     private readonly TypeMapper _typeMapper;
      8     private readonly MetadataTools _ef;
      9 
     10     public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)
     11     {
     12         ArgumentNotNull(code, "code");
     13         ArgumentNotNull(typeMapper, "typeMapper");
     14         ArgumentNotNull(ef, "ef");
     15 
     16         _code = code;
     17         _typeMapper = typeMapper;
     18         _ef = ef;
     19     }
     20 
     21     public string Property(EdmProperty edmProperty)
     22     {
     23         return string.Format(
     24             CultureInfo.InvariantCulture,
     25             "{0} {1} {2} {{ {3}get; {4}set; }}",
     26             Accessibility.ForProperty(edmProperty),
     27             _typeMapper.GetTypeName(edmProperty.TypeUsage),
     28             _code.Escape(edmProperty),
     29             _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
     30             _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
     31     }
     32 
     33     public string NavigationProperty(NavigationProperty navigationProperty)
     34     {
     35         var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());
     36         return string.Format(
     37             CultureInfo.InvariantCulture,
     38             "{0} {1} {2} {{ {3}get; {4}set; }}",
     39             AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
     40             navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
     41             _code.Escape(navigationProperty),
     42             _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
     43             _code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
     44     }
     45     
     46     public string AccessibilityAndVirtual(string accessibility)
     47     {
     48         return accessibility + (accessibility != "private" ? " virtual" : "");
     49     }
     50     
     51     public string EntityClassOpening(EntityType entity)
     52     {
     53         return string.Format(
     54             CultureInfo.InvariantCulture,
     55             "{0} {1}partial class {2}{3}",
     56             Accessibility.ForType(entity),
     57             _code.SpaceAfter(_code.AbstractOption(entity)),
     58             _code.Escape(entity),
     59             _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
     60     }
     61     
     62     public string EnumOpening(SimpleType enumType)
     63     {
     64         return string.Format(
     65             CultureInfo.InvariantCulture,
     66             "{0} enum {1} : {2}",
     67             Accessibility.ForType(enumType),
     68             _code.Escape(enumType),
     69             _code.Escape(_typeMapper.UnderlyingClrType(enumType)));
     70         }
     71     
     72     public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)
     73     {
     74         var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
     75         foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))
     76         {
     77             var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null";
     78             var notNullInit = "new ObjectParameter("" + parameter.EsqlParameterName + "", " + parameter.FunctionParameterName + ")";
     79             var nullInit = "new ObjectParameter("" + parameter.EsqlParameterName + "", typeof(" + parameter.RawClrTypeName + "))";
     80             writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);
     81         }
     82     }
     83     
     84     public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)
     85     {
     86         var parameters = _typeMapper.GetParameters(edmFunction);
     87         
     88         return string.Format(
     89             CultureInfo.InvariantCulture,
     90             "{0} IQueryable<{1}> {2}({3})",
     91             AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
     92             _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
     93             _code.Escape(edmFunction),
     94             string.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()));
     95     }
     96     
     97     public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)
     98     {
     99         var parameters = _typeMapper.GetParameters(edmFunction);
    100         
    101         return string.Format(
    102             CultureInfo.InvariantCulture,
    103             "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>("[{1}].[{2}]({3})"{4});",
    104             _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
    105             edmFunction.NamespaceName,
    106             edmFunction.Name,
    107             string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()),
    108             _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())));
    109     }
    110     
    111     public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
    112     {
    113         var parameters = _typeMapper.GetParameters(edmFunction);
    114         var returnType = _typeMapper.GetReturnType(edmFunction);
    115 
    116         var paramList = String.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray());
    117         if (includeMergeOption)
    118         {
    119             paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption";
    120         }
    121 
    122         return string.Format(
    123             CultureInfo.InvariantCulture,
    124             "{0} {1} {2}({3})",
    125             AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
    126             returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
    127             _code.Escape(edmFunction),
    128             paramList);
    129     }
    130     
    131     public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
    132     {
    133         var parameters = _typeMapper.GetParameters(edmFunction);
    134         var returnType = _typeMapper.GetReturnType(edmFunction);
    135 
    136         var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
    137         if (includeMergeOption)
    138         {
    139             callParams = ", mergeOption" + callParams;
    140         }
    141         
    142         return string.Format(
    143             CultureInfo.InvariantCulture,
    144             "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}("{1}"{2});",
    145             returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
    146             edmFunction.Name,
    147             callParams);
    148     }
    149     
    150     public string DbSet(EntitySet entitySet)
    151     {
    152         return string.Format(
    153             CultureInfo.InvariantCulture,
    154             "{0} DbSet<{1}> {2} {{ get; set; }}",
    155             Accessibility.ForReadOnlyProperty(entitySet),
    156             _typeMapper.GetTypeName(entitySet.ElementType),
    157             _code.Escape(entitySet));
    158     }
    159 
    160     public string UsingDirectives(bool inHeader, bool includeCollections = true)
    161     {
    162         return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
    163             ? string.Format(
    164                 CultureInfo.InvariantCulture,
    165                 "{0}using System;{1}" +
    166                 "{2}",
    167                 inHeader ? Environment.NewLine : "",
    168                 includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
    169                 inHeader ? "" : Environment.NewLine)
    170             : "";
    171     }
    172 }
    173 
    174 public class TypeMapper
    175 {
    176     private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName";
    177 
    178     private readonly System.Collections.IList _errors;
    179     private readonly CodeGenerationTools _code;
    180     private readonly MetadataTools _ef;
    181 
    182     public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)
    183     {
    184         ArgumentNotNull(code, "code");
    185         ArgumentNotNull(ef, "ef");
    186         ArgumentNotNull(errors, "errors");
    187 
    188         _code = code;
    189         _ef = ef;
    190         _errors = errors;
    191     }
    192 
    193     public string GetTypeName(TypeUsage typeUsage)
    194     {
    195         return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);
    196     }
    197 
    198     public string GetTypeName(EdmType edmType)
    199     {
    200         return GetTypeName(edmType, isNullable: null, modelNamespace: null);
    201     }
    202 
    203     public string GetTypeName(TypeUsage typeUsage, string modelNamespace)
    204     {
    205         return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);
    206     }
    207 
    208     public string GetTypeName(EdmType edmType, string modelNamespace)
    209     {
    210         return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);
    211     }
    212 
    213     public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)
    214     {
    215         if (edmType == null)
    216         {
    217             return null;
    218         }
    219 
    220         var collectionType = edmType as CollectionType;
    221         if (collectionType != null)
    222         {
    223             return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace));
    224         }
    225 
    226         var typeName = _code.Escape(edmType.MetadataProperties
    227                                 .Where(p => p.Name == ExternalTypeNameAttributeName)
    228                                 .Select(p => (string)p.Value)
    229                                 .FirstOrDefault())
    230             ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?
    231                 _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :
    232                 _code.Escape(edmType));
    233 
    234         if (edmType is StructuralType)
    235         {
    236             return typeName;
    237         }
    238 
    239         if (edmType is SimpleType)
    240         {
    241             var clrType = UnderlyingClrType(edmType);
    242             if (!IsEnumType(edmType))
    243             {
    244                 typeName = _code.Escape(clrType);
    245             }
    246 
    247             return clrType.IsValueType && isNullable == true ?
    248                 String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) :
    249                 typeName;
    250         }
    251 
    252         throw new ArgumentException("edmType");
    253     }
    254     
    255     public Type UnderlyingClrType(EdmType edmType)
    256     {
    257         ArgumentNotNull(edmType, "edmType");
    258 
    259         var primitiveType = edmType as PrimitiveType;
    260         if (primitiveType != null)
    261         {
    262             return primitiveType.ClrEquivalentType;
    263         }
    264 
    265         if (IsEnumType(edmType))
    266         {
    267             return GetEnumUnderlyingType(edmType).ClrEquivalentType;
    268         }
    269 
    270         return typeof(object);
    271     }
    272     
    273     public object GetEnumMemberValue(MetadataItem enumMember)
    274     {
    275         ArgumentNotNull(enumMember, "enumMember");
    276         
    277         var valueProperty = enumMember.GetType().GetProperty("Value");
    278         return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);
    279     }
    280     
    281     public string GetEnumMemberName(MetadataItem enumMember)
    282     {
    283         ArgumentNotNull(enumMember, "enumMember");
    284         
    285         var nameProperty = enumMember.GetType().GetProperty("Name");
    286         return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);
    287     }
    288 
    289     public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)
    290     {
    291         ArgumentNotNull(enumType, "enumType");
    292 
    293         var membersProperty = enumType.GetType().GetProperty("Members");
    294         return membersProperty != null 
    295             ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)
    296             : Enumerable.Empty<MetadataItem>();
    297     }
    298     
    299     public bool EnumIsFlags(EdmType enumType)
    300     {
    301         ArgumentNotNull(enumType, "enumType");
    302         
    303         var isFlagsProperty = enumType.GetType().GetProperty("IsFlags");
    304         return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);
    305     }
    306 
    307     public bool IsEnumType(GlobalItem edmType)
    308     {
    309         ArgumentNotNull(edmType, "edmType");
    310 
    311         return edmType.GetType().Name == "EnumType";
    312     }
    313 
    314     public PrimitiveType GetEnumUnderlyingType(EdmType enumType)
    315     {
    316         ArgumentNotNull(enumType, "enumType");
    317 
    318         return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null);
    319     }
    320 
    321     public string CreateLiteral(object value)
    322     {
    323         if (value == null || value.GetType() != typeof(TimeSpan))
    324         {
    325             return _code.CreateLiteral(value);
    326         }
    327 
    328         return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks);
    329     }
    330     
    331     public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)
    332     {
    333         ArgumentNotNull(types, "types");
    334         ArgumentNotNull(sourceFile, "sourceFile");
    335         
    336         var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
    337         if (types.Any(item => !hash.Add(item)))
    338         {
    339             _errors.Add(
    340                 new CompilerError(sourceFile, -1, -1, "6023",
    341                     String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict"))));
    342             return false;
    343         }
    344         return true;
    345     }
    346     
    347     public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)
    348     {
    349         return GetItemsToGenerate<SimpleType>(itemCollection)
    350             .Where(e => IsEnumType(e));
    351     }
    352     
    353     public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType
    354     {
    355         return itemCollection
    356             .OfType<T>()
    357             .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))
    358             .OrderBy(i => i.Name);
    359     }
    360 
    361     public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)
    362     {
    363         return itemCollection
    364             .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))
    365             .Select(g => GetGlobalItemName(g));
    366     }
    367 
    368     public string GetGlobalItemName(GlobalItem item)
    369     {
    370         if (item is EdmType)
    371         {
    372             return ((EdmType)item).Name;
    373         }
    374         else
    375         {
    376             return ((EntityContainer)item).Name;
    377         }
    378     }
    379 
    380     public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)
    381     {
    382         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
    383     }
    384     
    385     public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)
    386     {
    387         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
    388     }
    389     
    390     public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)
    391     {
    392         return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
    393     }
    394     
    395     public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)
    396     {
    397         return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
    398     }
    399 
    400     public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)
    401     {
    402         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
    403     }
    404     
    405     public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)
    406     {
    407         return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
    408     }
    409 
    410     public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)
    411     {
    412         return type.NavigationProperties.Where(np => np.DeclaringType == type);
    413     }
    414     
    415     public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)
    416     {
    417         return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
    418     }
    419     
    420     public FunctionParameter GetReturnParameter(EdmFunction edmFunction)
    421     {
    422         ArgumentNotNull(edmFunction, "edmFunction");
    423 
    424         var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters");
    425         return returnParamsProperty == null
    426             ? edmFunction.ReturnParameter
    427             : ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();
    428     }
    429 
    430     public bool IsComposable(EdmFunction edmFunction)
    431     {
    432         ArgumentNotNull(edmFunction, "edmFunction");
    433 
    434         var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute");
    435         return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);
    436     }
    437 
    438     public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)
    439     {
    440         return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
    441     }
    442 
    443     public TypeUsage GetReturnType(EdmFunction edmFunction)
    444     {
    445         var returnParam = GetReturnParameter(edmFunction);
    446         return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);
    447     }
    448     
    449     public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)
    450     {
    451         var returnType = GetReturnType(edmFunction);
    452         return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;
    453     }
    454 }
    455 
    456 public class EdmMetadataLoader
    457 {
    458     private readonly IDynamicHost _host;
    459     private readonly System.Collections.IList _errors;
    460 
    461     public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors)
    462     {
    463         ArgumentNotNull(host, "host");
    464         ArgumentNotNull(errors, "errors");
    465 
    466         _host = host;
    467         _errors = errors;
    468     }
    469 
    470     public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath)
    471     {
    472         ArgumentNotNull(sourcePath, "sourcePath");
    473 
    474         if (!ValidateInputPath(sourcePath))
    475         {
    476             return new EdmItemCollection();
    477         }
    478 
    479         var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath));
    480         if (schemaElement != null)
    481         {
    482             using (var reader = schemaElement.CreateReader())
    483             {
    484                 IList<EdmSchemaError> errors;
    485                 var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors);
    486 
    487                 ProcessErrors(errors, sourcePath);
    488 
    489                 return itemCollection;
    490             }
    491         }
    492         return new EdmItemCollection();
    493     }
    494 
    495     public string GetModelNamespace(string sourcePath)
    496     {
    497         ArgumentNotNull(sourcePath, "sourcePath");
    498 
    499         if (!ValidateInputPath(sourcePath))
    500         {
    501             return string.Empty;
    502         }
    503 
    504         var model = LoadRootElement(_host.ResolvePath(sourcePath));
    505         if (model == null)
    506         {
    507             return string.Empty;
    508         }
    509 
    510         var attribute = model.Attribute("Namespace");
    511         return attribute != null ? attribute.Value : "";
    512     }
    513 
    514     private bool ValidateInputPath(string sourcePath)
    515     {
    516         if (sourcePath == "$" + "edmxInputFile" + "$")
    517         {
    518             _errors.Add(
    519                 new CompilerError(_host.TemplateFile ?? sourcePath, 0, 0, string.Empty,
    520                     GetResourceString("Template_ReplaceVsItemTemplateToken")));
    521             return false;
    522         }
    523 
    524         return true;
    525     }
    526 
    527     public XElement LoadRootElement(string sourcePath)
    528     {
    529         ArgumentNotNull(sourcePath, "sourcePath");
    530 
    531         var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
    532         return root.Elements()
    533             .Where(e => e.Name.LocalName == "Runtime")
    534             .Elements()
    535             .Where(e => e.Name.LocalName == "ConceptualModels")
    536             .Elements()
    537             .Where(e => e.Name.LocalName == "Schema")
    538             .FirstOrDefault()
    539                 ?? root;
    540     }
    541 
    542     private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath)
    543     {
    544         foreach (var error in errors)
    545         {
    546             _errors.Add(
    547                 new CompilerError(
    548                     error.SchemaLocation ?? sourceFilePath,
    549                     error.Line,
    550                     error.Column,
    551                     error.ErrorCode.ToString(CultureInfo.InvariantCulture),
    552                     error.Message)
    553                 {
    554                     IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning
    555                 });
    556         }
    557     }
    558     
    559     public bool IsLazyLoadingEnabled(EntityContainer container)
    560     {
    561         string lazyLoadingAttributeValue;
    562         var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled";
    563         bool isLazyLoading;
    564         return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, out lazyLoadingAttributeValue)
    565             || !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading)
    566             || isLazyLoading;
    567     }
    568 }
    569 
    570 public static void ArgumentNotNull<T>(T arg, string name) where T : class
    571 {
    572     if (arg == null)
    573     {
    574         throw new ArgumentNullException(name);
    575     }
    576 }
    577     
    578 private static readonly Lazy<System.Resources.ResourceManager> ResourceManager =
    579     new Lazy<System.Resources.ResourceManager>(
    580         () => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true);
    581 public static TypeMapper GlobaltypeMapper{get;set;}
    582 public static CodeStringGenerator codeStringGenerator{get;set;}
    583 public static CodeGenerationTools GlobalCode{get;set;}
    584 public static string GetResourceString(string resourceName)
    585 {
    586     ArgumentNotNull(resourceName, "resourceName");
    587 
    588     return ResourceManager.Value.GetString(resourceName, null);
    589 }
    590 #>
    基础函数
     1 <#@ template language="C#" debug="True" #>
     2 <#@ assembly name="System.Core" #>
     3 <#@ import namespace="System.IO" #>
     4 <#@ import namespace="System.Linq" #>
     5 <#@ import namespace="System.Text" #>
     6 <#@ output extension="cs" #>
     7 <#@ include file="T4Toolbox.tt" #>
     8 <#@ include file="EntityTemplate.tt" #>
     9 <#@ output extension=".cs" encoding="UTF-8"#>
    10 <#
    11 
    12     string curPath = Path.GetDirectoryName(Host.TemplateFile);
    13     string destPath = Path.Combine(curPath, @"..");
    14     if(!Directory.Exists(destPath))
    15     {
    16         Directory.CreateDirectory(destPath);
    17     }
    18     const string inputFile = @"....DataMouldSQLServer.edmx";
    19     var textTransform = DynamicTextTransformation.Create(this);
    20     var code = new CodeGenerationTools(this);
    21     GlobalCode=new CodeGenerationTools(this);
    22     var ef = new MetadataTools(this);
    23     GlobaltypeMapper = new TypeMapper(code, ef, textTransform.Errors);
    24     codeStringGenerator = new CodeStringGenerator(code, GlobaltypeMapper, ef);
    25     var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
    26     foreach (var entity in GlobaltypeMapper.GetItemsToGenerate<EntityType>(itemCollection))
    27     {
    28         EntityTemplate template = new EntityTemplate(entity);
    29         string fileName = string.Format(@"{0}{1}.cs", destPath, entity.Name);
    30         template.Output.Encoding = Encoding.UTF8;
    31         if(File.Exists(fileName))
    32         {
    33             File.Delete(fileName);
    34         }
    35         template.RenderToFile(fileName);
    36     }
    37 #>
    代码生成
     1 <#@ template language="C#" debug="True" #>
     2 <#@ output extension="cs" #>
     3 <#@ include file="BaseClassTemplate.tt" #>
     4 
     5 <#+
     6 // <copyright file="EntityTemplate.tt" company="">
     7 //  Copyright © . All Rights Reserved.
     8 // </copyright>
     9 
    10 public class EntityTemplate : CSharpTemplate
    11 {
    12     
    13     private EntityType _entitis;
    14     public EntityTemplate(EntityType Entitis)
    15     {
    16         _entitis = Entitis;
    17     }
    18 
    19     public override string TransformText()
    20     {
    21         base.TransformText();
    22 #>
    23 using System;
    24 using System.Collections.Generic;
    25 namespace Entities
    26 {
    27     public class <#= _entitis.Name #>
    28     {
    29 <#+
    30     var propertiesWithDefaultValues = GlobaltypeMapper.GetPropertiesWithDefaultValues(_entitis);
    31     var collectionNavigationProperties = GlobaltypeMapper.GetCollectionNavigationProperties(_entitis);
    32     var complexProperties = GlobaltypeMapper.GetComplexProperties(_entitis);
    33 
    34     if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())
    35     {
    36 #>
    37         public <#=GlobalCode.Escape(_entitis)#>()
    38         {
    39 <#+
    40         foreach (var edmProperty in propertiesWithDefaultValues)
    41         {
    42 #>
    43             this.<#=GlobalCode.Escape(edmProperty)#> = <#=GlobaltypeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
    44 <#+
    45         }
    46 
    47         foreach (var navigationProperty in collectionNavigationProperties)
    48         {
    49 #>
    50             this.<#=GlobalCode.Escape(navigationProperty)#> = new HashSet<<#=GlobaltypeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
    51 <#+
    52         }
    53 
    54         foreach (var complexProperty in complexProperties)
    55         {
    56 #>
    57             this.<#=GlobalCode.Escape(complexProperty)#> = new <#=GlobaltypeMapper.GetTypeName(complexProperty.TypeUsage)#>();
    58 <#+
    59         }
    60 #>
    61         }        
    62 <#+
    63     }
    64         var simpleProperties = GlobaltypeMapper.GetSimpleProperties(_entitis);
    65         //var propertiesWithDefaultValues = GlobaltypeMapper.GetPropertiesWithDefaultValues(_entitis);
    66         var navigationProperties = GlobaltypeMapper.GetNavigationProperties(_entitis);
    67         
    68         foreach(var property in simpleProperties)
    69         {
    70 #>
    71         <#=codeStringGenerator.Property(property)#>
    72 <#+
    73         }
    74         
    75     if (navigationProperties.Any())
    76     {
    77 #>
    78 
    79 <#+
    80         foreach (var navigationProperty in navigationProperties)
    81         {
    82 #>
    83         <#=codeStringGenerator.NavigationProperty(navigationProperty)#>
    84 <#+
    85         }
    86     }
    87         
    88         #>        
    89     }
    90 }
    91 <#+
    92         return this.GenerationEnvironment.ToString();
    93     }
    94 }
    95 #>
    Entity生成模板

      一个模板拆分成3个文件,我引入了T4toolbox作为代码着色器,这样,我可以在不同的层内,放置不同的模板,只需要修改生成模板的代码,并修改代码生成那里面的一段代码,比如生成DTO的模板是这样的

     1 <#@ template language="C#" debug="True" #>
     2 <#@ output extension="cs" #>
     3 <#@ include file="BaseClassTemplate.tt" #>
     4 
     5 <#+
     6 
     7 
     8 public class EntityTemplate : CSharpTemplate
     9 {
    10     
    11     private EntityType _entitis;
    12     public EntityTemplate(EntityType Entitis)
    13     {
    14         _entitis = Entitis;
    15     }
    16 
    17     public override string TransformText()
    18     {
    19         base.TransformText();
    20 #>
    21 using System;
    22 using System.Collections.Generic;
    23 namespace DTO
    24 {
    25     public class <#= _entitis.Name.Replace("T_","")+"DTO" #>
    26     {        
    27 <#+
    28         
    29         var simpleProperties = GlobaltypeMapper.GetSimpleProperties(_entitis);
    30         //var propertiesWithDefaultValues = GlobaltypeMapper.GetPropertiesWithDefaultValues(_entitis);
    31         var navigationProperties = GlobaltypeMapper.GetNavigationProperties(_entitis);
    32         var complexProperties = GlobaltypeMapper.GetComplexProperties(_entitis);
    33         foreach(var property in simpleProperties)
    34         {
    35 #>
    36         <#=codeStringGenerator.Property(property)#>
    37 <#+
    38         }
    39         
    40         
    41         #>        
    42     }
    43 }
    44 <#+
    45         return this.GenerationEnvironment.ToString();
    46     }
    47 }
    48 #>
    DTO生成模板

      修改代码生成中的语句为

    string fileName = string.Format(@"{0}{1}.cs", destPath, entity.Name.Replace("T_","")+"DTO");
    

      2.我尝试用原有的事务调用的方式去修改dbcontext

    比如在IDataBase方法下增加了三个函数

     1 void BeginTransaction();
     2 void CommitTransaction();
     3 void RollbackTransaction();

    实现

     1 protected static DbEntities _dbEntities { get; set; }//我定义的dbcontext的基类
     2 protected DbConnection _con { get; set; }
     3 protected DbTransaction _tran { get; set; }
     4 public BaseDao()
     5 {
     6      _dbEntities = new DbEntities();
     7 }
     8 public void BeginTransaction()
     9 {
    10     _con = ((IObjectContextAdapter)_dbEntities).ObjectContext.Connection;
    11     _con.Open();
    12     _tran=_con.BeginTransaction();
    13 }
    14 
    15 public void CommitTransaction()
    16 {
    17     _tran.Commit();
    18     _con.Close();
    19 }
    20 public void RollbackTransaction()
    21 {
    22     _tran.Rollback();
    23     _con.Close();
    24 }

      当然,DbContext本身的savechange已经实现了事务,又或者我可以采用别的园友使用的unitofwork的方式来进行事物封装,但是在选择之前,我只想知道能不能用传统的方式来实现

      3.Linq to Entities,这是个好东西,可以将linq或者lambda语句转换成实际运行的SQL,让程序员不需要太多关心语句怎么去组织,虽然用三层的方式来调用很违和,但是没必要放弃这个特性

    以查询为例

     1        /// <summary>
     2        /// 根据条件获取数据实体集合
     3        /// </summary>
     4        /// <typeparam name="T">实体类型</typeparam>
     5        /// <param name="exp">条件表达式</param>
     6        /// <param name="useBaseContext">是否使用类基础的context,以达到延迟加载的目的</param>
     7        /// <returns></returns>
     8         public virtual IQueryable<T> GetEntities<T>(Expression<Func<T, bool>> exp, bool useBaseContext=false) where T : class
     9         {
    10             if (useBaseContext)
    11             {
    12                 return _dbEntities.Set<T>().Where(exp);
    13             }
    14             using (DbEntities db = new DbEntities())
    15             {
    16                 return db.Set<T>().Where(exp);
    17             }
    18         }   

      如果使用传统三层或者事务脚本的模式,肯定涉及到分层,有时候分层代码是放在不同的项目中,因为业务需求,你需要在Service里面编写linq或者lambda,如果直接使用using的方式,很多时候会提示你DbContext已经关闭,所以我这里提供了两种方式,如果需要用延迟加载的方式,那么设置使用基类的DbContext来提供链接.因为我采用了IQueryalbe来作为返回,这样可以在需要懒加载的时候传递表达式目录树。至于这种用法DbContext释放的问题和是否需要释放的问题,我们下一步再讨论。

    调用方法(比如直接转化成DTO)

    1     public IEnumerable<UserDTO> GetList()
    2         {
    3 
    4             return (from a in _dal.GetEntities<T_User>(m=>m.T_UserID==1, true)
    5                     select new UserDTO 
    6                    { 
    7                        T_UserID=a.T_UserID
    8                    }).ToList();
    9         } 

    生成的语句

    这里要注意,linq语句里面不能直接用select new T_User{}或者lambda语句中用.select(m=>new T_User{})来传递表达式目录树实现选定字段的查询,因为在DbContext里面已经已经声明了DBSet<T_User> T_User,在lazy load的时候,Linq to Entities会把T_User当成这个DBSet的实例来构建表达式目录树,导致转换SQL语句过程失败。

    提示:The entity or complex type 'SqlServer.T_User' cannot be constructed in a LINQ to Entities query.

      几个存在的问题:

      1.传统的DAL层会将每一个实体的操作抽象成一个独立的数据对象,在用EF构建的DAL层里,当然也可以用类似 T_UserDao.cs的方式来建立这个链接,方便以后转换ORM组件,但是建立这些类似public T_UserDAO:BaseDAO{}的空链接文件有没有必要?BaseDAO甚至直接将DBSet暴露给Service似乎更适合EF。

      2.是否适合用传统的方式来使用EF?

      3.非using方式使用DbContext是否真的会产生不必要的错误?如果要用到using的方式,应该怎样改造这个框架呢?

  • 相关阅读:
    python基础之元组、文件操作、编码、函数、变量
    python---基础之模块,列表,元组,字典
    python成长之路-----day1-----作业(登录程序和三级菜单)
    k8s的port、targetport、nodeport之间的区别
    查找并删除文件
    systemctl自定义service
    中标麒麟7.0源
    springboot问题,没有主清单属性
    iso搭建本地源
    添加路由
  • 原文地址:https://www.cnblogs.com/redfoxhuang/p/4395774.html
Copyright © 2020-2023  润新知