框架计划随笔 三.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 = @"..\..\DataMould\SQLServer.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的方式,应该怎样改造这个框架呢?

 

posted @ 2015-04-06 14:25  小不正经  阅读(344)  评论(0编辑  收藏  举报