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

时间:2023-12-18 15:50:08

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

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

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

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

  1.T4脚本分离

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

 <#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#><# const string inputFile = @"SqlServer.edmx";
var textTransform = DynamicTextTransformation.Create(this);
var code = new CodeGenerationTools(this);
var ef = new MetadataTools(this);
var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
var fileManager = EntityFrameworkTemplateFileManager.Create(this);
var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile))
{
return string.Empty;
} WriteHeader(codeStringGenerator, fileManager); foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
fileManager.StartNewFile(entity.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
<#=codeStringGenerator.EntityClassOpening(entity)#>
{
<#
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);
var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);
var complexProperties = typeMapper.GetComplexProperties(entity); if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())
{
#>
public <#=code.Escape(entity)#>()
{
<#
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
<#
} foreach (var navigationProperty in collectionNavigationProperties)
{
#>
this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
<#
} foreach (var complexProperty in complexProperties)
{
#>
this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
<#
}
#>
} <#
} var simpleProperties = typeMapper.GetSimpleProperties(entity);
if (simpleProperties.Any())
{
foreach (var edmProperty in simpleProperties)
{
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
} if (complexProperties.Any())
{
#> <#
foreach(var complexProperty in complexProperties)
{
#>
<#=codeStringGenerator.Property(complexProperty)#>
<#
}
} var navigationProperties = typeMapper.GetNavigationProperties(entity);
if (navigationProperties.Any())
{
#> <#
foreach (var navigationProperty in navigationProperties)
{
#>
<#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#
}
}
#>
}
<#
EndNamespace(code);
} foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection))
{
fileManager.StartNewFile(complex.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
{
<#
var complexProperties = typeMapper.GetComplexProperties(complex);
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); if (propertiesWithDefaultValues.Any() || complexProperties.Any())
{
#>
public <#=code.Escape(complex)#>()
{
<#
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
<#
} foreach (var complexProperty in complexProperties)
{
#>
this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
<#
}
#>
} <#
} var simpleProperties = typeMapper.GetSimpleProperties(complex);
if (simpleProperties.Any())
{
foreach(var edmProperty in simpleProperties)
{
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
} if (complexProperties.Any())
{
#> <#
foreach(var edmProperty in complexProperties)
{
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
}
#>
}
<#
EndNamespace(code);
} foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection))
{
fileManager.StartNewFile(enumType.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
<#
if (typeMapper.EnumIsFlags(enumType))
{
#>
[Flags]
<#
}
#>
<#=codeStringGenerator.EnumOpening(enumType)#>
{
<#
var foundOne = false; foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType))
{
foundOne = true;
#>
<#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>,
<#
} if (foundOne)
{
this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - , );
}
#>
}
<#
EndNamespace(code);
} fileManager.Process(); #>
<#+ public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager)
{
fileManager.StartHeader();
#>
//------------------------------------------------------------------------------
// <auto-generated>
// <#=GetResourceString("Template_GeneratedCodeCommentLine1")#>
//
// <#=GetResourceString("Template_GeneratedCodeCommentLine2")#>
// <#=GetResourceString("Template_GeneratedCodeCommentLine3")#>
// </auto-generated>
//------------------------------------------------------------------------------
<#=codeStringGenerator.UsingDirectives(inHeader: true)#>
<#+
fileManager.EndBlock();
} public void BeginNamespace(CodeGenerationTools code)
{
var codeNamespace = code.VsNamespaceSuggestion();
if (!String.IsNullOrEmpty(codeNamespace))
{
#>
namespace <#=code.EscapeNamespace(codeNamespace)#>
{
<#+
PushIndent(" ");
}
} public void EndNamespace(CodeGenerationTools code)
{
if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion()))
{
PopIndent();
#>
}
<#+
}
} public const string TemplateId = "CSharp_DbContext_Types_EF5"; public class CodeStringGenerator
{
private readonly CodeGenerationTools _code;
private readonly TypeMapper _typeMapper;
private readonly MetadataTools _ef; public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)
{
ArgumentNotNull(code, "code");
ArgumentNotNull(typeMapper, "typeMapper");
ArgumentNotNull(ef, "ef"); _code = code;
_typeMapper = typeMapper;
_ef = ef;
} public string Property(EdmProperty edmProperty)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
} public string NavigationProperty(NavigationProperty navigationProperty)
{
var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
_code.Escape(navigationProperty),
_code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
_code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
} public string AccessibilityAndVirtual(string accessibility)
{
return accessibility + (accessibility != "private" ? " virtual" : "");
} public string EntityClassOpening(EntityType entity)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
} public string EnumOpening(SimpleType enumType)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} enum {1} : {2}",
Accessibility.ForType(enumType),
_code.Escape(enumType),
_code.Escape(_typeMapper.UnderlyingClrType(enumType)));
} public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)
{
var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))
{
var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null";
var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")";
var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + parameter.RawClrTypeName + "))";
writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);
}
} public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)
{
var parameters = _typeMapper.GetParameters(edmFunction); return string.Format(
CultureInfo.InvariantCulture,
"{0} IQueryable<{1}> {2}({3})",
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
_code.Escape(edmFunction),
string.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()));
} public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)
{
var parameters = _typeMapper.GetParameters(edmFunction); return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});",
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
edmFunction.NamespaceName,
edmFunction.Name,
string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()),
_code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())));
} public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction); var paramList = String.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray());
if (includeMergeOption)
{
paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption";
} return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2}({3})",
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
_code.Escape(edmFunction),
paramList);
} public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction); var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
if (includeMergeOption)
{
callParams = ", mergeOption" + callParams;
} return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});",
returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
edmFunction.Name,
callParams);
} public string DbSet(EntitySet entitySet)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} DbSet<{1}> {2} {{ get; set; }}",
Accessibility.ForReadOnlyProperty(entitySet),
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet));
} public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
? string.Format(
CultureInfo.InvariantCulture,
"{0}using System;{1}" +
"{2}",
inHeader ? Environment.NewLine : "",
includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
inHeader ? "" : Environment.NewLine)
: "";
}
} public class TypeMapper
{
private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; private readonly System.Collections.IList _errors;
private readonly CodeGenerationTools _code;
private readonly MetadataTools _ef; public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)
{
ArgumentNotNull(code, "code");
ArgumentNotNull(ef, "ef");
ArgumentNotNull(errors, "errors"); _code = code;
_ef = ef;
_errors = errors;
} public string GetTypeName(TypeUsage typeUsage)
{
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);
} public string GetTypeName(EdmType edmType)
{
return GetTypeName(edmType, isNullable: null, modelNamespace: null);
} public string GetTypeName(TypeUsage typeUsage, string modelNamespace)
{
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);
} public string GetTypeName(EdmType edmType, string modelNamespace)
{
return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);
} public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)
{
if (edmType == null)
{
return null;
} var collectionType = edmType as CollectionType;
if (collectionType != null)
{
return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace));
} var typeName = _code.Escape(edmType.MetadataProperties
.Where(p => p.Name == ExternalTypeNameAttributeName)
.Select(p => (string)p.Value)
.FirstOrDefault())
?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?
_code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :
_code.Escape(edmType)); if (edmType is StructuralType)
{
return typeName;
} if (edmType is SimpleType)
{
var clrType = UnderlyingClrType(edmType);
if (!IsEnumType(edmType))
{
typeName = _code.Escape(clrType);
} return clrType.IsValueType && isNullable == true ?
String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) :
typeName;
} throw new ArgumentException("edmType");
} public Type UnderlyingClrType(EdmType edmType)
{
ArgumentNotNull(edmType, "edmType"); var primitiveType = edmType as PrimitiveType;
if (primitiveType != null)
{
return primitiveType.ClrEquivalentType;
} if (IsEnumType(edmType))
{
return GetEnumUnderlyingType(edmType).ClrEquivalentType;
} return typeof(object);
} public object GetEnumMemberValue(MetadataItem enumMember)
{
ArgumentNotNull(enumMember, "enumMember"); var valueProperty = enumMember.GetType().GetProperty("Value");
return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);
} public string GetEnumMemberName(MetadataItem enumMember)
{
ArgumentNotNull(enumMember, "enumMember"); var nameProperty = enumMember.GetType().GetProperty("Name");
return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);
} public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType"); var membersProperty = enumType.GetType().GetProperty("Members");
return membersProperty != null
? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)
: Enumerable.Empty<MetadataItem>();
} public bool EnumIsFlags(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType"); var isFlagsProperty = enumType.GetType().GetProperty("IsFlags");
return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);
} public bool IsEnumType(GlobalItem edmType)
{
ArgumentNotNull(edmType, "edmType"); return edmType.GetType().Name == "EnumType";
} public PrimitiveType GetEnumUnderlyingType(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType"); return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null);
} public string CreateLiteral(object value)
{
if (value == null || value.GetType() != typeof(TimeSpan))
{
return _code.CreateLiteral(value);
} return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks);
} public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)
{
ArgumentNotNull(types, "types");
ArgumentNotNull(sourceFile, "sourceFile"); var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
if (types.Any(item => !hash.Add(item)))
{
_errors.Add(
new CompilerError(sourceFile, -, -, "",
String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict"))));
return false;
}
return true;
} public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)
{
return GetItemsToGenerate<SimpleType>(itemCollection)
.Where(e => IsEnumType(e));
} public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType
{
return itemCollection
.OfType<T>()
.Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))
.OrderBy(i => i.Name);
} public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)
{
return itemCollection
.Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))
.Select(g => GetGlobalItemName(g));
} public string GetGlobalItemName(GlobalItem item)
{
if (item is EdmType)
{
return ((EdmType)item).Name;
}
else
{
return ((EntityContainer)item).Name;
}
} public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
} public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
} public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)
{
return type.NavigationProperties.Where(np => np.DeclaringType == type);
} public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)
{
return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
} public FunctionParameter GetReturnParameter(EdmFunction edmFunction)
{
ArgumentNotNull(edmFunction, "edmFunction"); var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters");
return returnParamsProperty == null
? edmFunction.ReturnParameter
: ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();
} public bool IsComposable(EdmFunction edmFunction)
{
ArgumentNotNull(edmFunction, "edmFunction"); var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute");
return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);
} public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)
{
return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
} public TypeUsage GetReturnType(EdmFunction edmFunction)
{
var returnParam = GetReturnParameter(edmFunction);
return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);
} public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)
{
var returnType = GetReturnType(edmFunction);
return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;
}
} public class EdmMetadataLoader
{
private readonly IDynamicHost _host;
private readonly System.Collections.IList _errors; public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors)
{
ArgumentNotNull(host, "host");
ArgumentNotNull(errors, "errors"); _host = host;
_errors = errors;
} public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath)
{
ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath))
{
return new EdmItemCollection();
} var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath));
if (schemaElement != null)
{
using (var reader = schemaElement.CreateReader())
{
IList<EdmSchemaError> errors;
var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors); ProcessErrors(errors, sourcePath); return itemCollection;
}
}
return new EdmItemCollection();
} public string GetModelNamespace(string sourcePath)
{
ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath))
{
return string.Empty;
} var model = LoadRootElement(_host.ResolvePath(sourcePath));
if (model == null)
{
return string.Empty;
} var attribute = model.Attribute("Namespace");
return attribute != null ? attribute.Value : "";
} private bool ValidateInputPath(string sourcePath)
{
if (sourcePath == "$" + "edmxInputFile" + "$")
{
_errors.Add(
new CompilerError(_host.TemplateFile ?? sourcePath, , , string.Empty,
GetResourceString("Template_ReplaceVsItemTemplateToken")));
return false;
} return true;
} public XElement LoadRootElement(string sourcePath)
{
ArgumentNotNull(sourcePath, "sourcePath"); var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
return root.Elements()
.Where(e => e.Name.LocalName == "Runtime")
.Elements()
.Where(e => e.Name.LocalName == "ConceptualModels")
.Elements()
.Where(e => e.Name.LocalName == "Schema")
.FirstOrDefault()
?? root;
} private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath)
{
foreach (var error in errors)
{
_errors.Add(
new CompilerError(
error.SchemaLocation ?? sourceFilePath,
error.Line,
error.Column,
error.ErrorCode.ToString(CultureInfo.InvariantCulture),
error.Message)
{
IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning
});
}
} public bool IsLazyLoadingEnabled(EntityContainer container)
{
string lazyLoadingAttributeValue;
var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled";
bool isLazyLoading;
return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, out lazyLoadingAttributeValue)
|| !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading)
|| isLazyLoading;
}
} public static void ArgumentNotNull<T>(T arg, string name) where T : class
{
if (arg == null)
{
throw new ArgumentNullException(name);
}
} private static readonly Lazy<System.Resources.ResourceManager> ResourceManager =
new Lazy<System.Resources.ResourceManager>(
() => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true); public static string GetResourceString(string resourceName)
{
ArgumentNotNull(resourceName, "resourceName"); return ResourceManager.Value.GetString(resourceName, null);
} #>

默认代码

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

 <#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#+
public class CodeStringGenerator
{
private readonly CodeGenerationTools _code;
private readonly TypeMapper _typeMapper;
private readonly MetadataTools _ef; public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)
{
ArgumentNotNull(code, "code");
ArgumentNotNull(typeMapper, "typeMapper");
ArgumentNotNull(ef, "ef"); _code = code;
_typeMapper = typeMapper;
_ef = ef;
} public string Property(EdmProperty edmProperty)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
} public string NavigationProperty(NavigationProperty navigationProperty)
{
var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
_code.Escape(navigationProperty),
_code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
_code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
} public string AccessibilityAndVirtual(string accessibility)
{
return accessibility + (accessibility != "private" ? " virtual" : "");
} public string EntityClassOpening(EntityType entity)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
} public string EnumOpening(SimpleType enumType)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} enum {1} : {2}",
Accessibility.ForType(enumType),
_code.Escape(enumType),
_code.Escape(_typeMapper.UnderlyingClrType(enumType)));
} public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)
{
var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))
{
var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null";
var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")";
var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + parameter.RawClrTypeName + "))";
writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);
}
} public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)
{
var parameters = _typeMapper.GetParameters(edmFunction); return string.Format(
CultureInfo.InvariantCulture,
"{0} IQueryable<{1}> {2}({3})",
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
_code.Escape(edmFunction),
string.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()));
} public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)
{
var parameters = _typeMapper.GetParameters(edmFunction); return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});",
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
edmFunction.NamespaceName,
edmFunction.Name,
string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()),
_code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())));
} public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction); var paramList = String.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray());
if (includeMergeOption)
{
paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption";
} return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2}({3})",
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
_code.Escape(edmFunction),
paramList);
} public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction); var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
if (includeMergeOption)
{
callParams = ", mergeOption" + callParams;
} return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});",
returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
edmFunction.Name,
callParams);
} public string DbSet(EntitySet entitySet)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} DbSet<{1}> {2} {{ get; set; }}",
Accessibility.ForReadOnlyProperty(entitySet),
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet));
} public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
? string.Format(
CultureInfo.InvariantCulture,
"{0}using System;{1}" +
"{2}",
inHeader ? Environment.NewLine : "",
includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
inHeader ? "" : Environment.NewLine)
: "";
}
} public class TypeMapper
{
private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; private readonly System.Collections.IList _errors;
private readonly CodeGenerationTools _code;
private readonly MetadataTools _ef; public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)
{
ArgumentNotNull(code, "code");
ArgumentNotNull(ef, "ef");
ArgumentNotNull(errors, "errors"); _code = code;
_ef = ef;
_errors = errors;
} public string GetTypeName(TypeUsage typeUsage)
{
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);
} public string GetTypeName(EdmType edmType)
{
return GetTypeName(edmType, isNullable: null, modelNamespace: null);
} public string GetTypeName(TypeUsage typeUsage, string modelNamespace)
{
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);
} public string GetTypeName(EdmType edmType, string modelNamespace)
{
return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);
} public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)
{
if (edmType == null)
{
return null;
} var collectionType = edmType as CollectionType;
if (collectionType != null)
{
return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace));
} var typeName = _code.Escape(edmType.MetadataProperties
.Where(p => p.Name == ExternalTypeNameAttributeName)
.Select(p => (string)p.Value)
.FirstOrDefault())
?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?
_code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :
_code.Escape(edmType)); if (edmType is StructuralType)
{
return typeName;
} if (edmType is SimpleType)
{
var clrType = UnderlyingClrType(edmType);
if (!IsEnumType(edmType))
{
typeName = _code.Escape(clrType);
} return clrType.IsValueType && isNullable == true ?
String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) :
typeName;
} throw new ArgumentException("edmType");
} public Type UnderlyingClrType(EdmType edmType)
{
ArgumentNotNull(edmType, "edmType"); var primitiveType = edmType as PrimitiveType;
if (primitiveType != null)
{
return primitiveType.ClrEquivalentType;
} if (IsEnumType(edmType))
{
return GetEnumUnderlyingType(edmType).ClrEquivalentType;
} return typeof(object);
} public object GetEnumMemberValue(MetadataItem enumMember)
{
ArgumentNotNull(enumMember, "enumMember"); var valueProperty = enumMember.GetType().GetProperty("Value");
return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);
} public string GetEnumMemberName(MetadataItem enumMember)
{
ArgumentNotNull(enumMember, "enumMember"); var nameProperty = enumMember.GetType().GetProperty("Name");
return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);
} public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType"); var membersProperty = enumType.GetType().GetProperty("Members");
return membersProperty != null
? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)
: Enumerable.Empty<MetadataItem>();
} public bool EnumIsFlags(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType"); var isFlagsProperty = enumType.GetType().GetProperty("IsFlags");
return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);
} public bool IsEnumType(GlobalItem edmType)
{
ArgumentNotNull(edmType, "edmType"); return edmType.GetType().Name == "EnumType";
} public PrimitiveType GetEnumUnderlyingType(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType"); return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null);
} public string CreateLiteral(object value)
{
if (value == null || value.GetType() != typeof(TimeSpan))
{
return _code.CreateLiteral(value);
} return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks);
} public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)
{
ArgumentNotNull(types, "types");
ArgumentNotNull(sourceFile, "sourceFile"); var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
if (types.Any(item => !hash.Add(item)))
{
_errors.Add(
new CompilerError(sourceFile, -, -, "",
String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict"))));
return false;
}
return true;
} public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)
{
return GetItemsToGenerate<SimpleType>(itemCollection)
.Where(e => IsEnumType(e));
} public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType
{
return itemCollection
.OfType<T>()
.Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))
.OrderBy(i => i.Name);
} public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)
{
return itemCollection
.Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))
.Select(g => GetGlobalItemName(g));
} public string GetGlobalItemName(GlobalItem item)
{
if (item is EdmType)
{
return ((EdmType)item).Name;
}
else
{
return ((EntityContainer)item).Name;
}
} public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
} public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
} public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
} public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)
{
return type.NavigationProperties.Where(np => np.DeclaringType == type);
} public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)
{
return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
} public FunctionParameter GetReturnParameter(EdmFunction edmFunction)
{
ArgumentNotNull(edmFunction, "edmFunction"); var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters");
return returnParamsProperty == null
? edmFunction.ReturnParameter
: ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();
} public bool IsComposable(EdmFunction edmFunction)
{
ArgumentNotNull(edmFunction, "edmFunction"); var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute");
return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);
} public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)
{
return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
} public TypeUsage GetReturnType(EdmFunction edmFunction)
{
var returnParam = GetReturnParameter(edmFunction);
return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);
} public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)
{
var returnType = GetReturnType(edmFunction);
return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;
}
} public class EdmMetadataLoader
{
private readonly IDynamicHost _host;
private readonly System.Collections.IList _errors; public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors)
{
ArgumentNotNull(host, "host");
ArgumentNotNull(errors, "errors"); _host = host;
_errors = errors;
} public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath)
{
ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath))
{
return new EdmItemCollection();
} var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath));
if (schemaElement != null)
{
using (var reader = schemaElement.CreateReader())
{
IList<EdmSchemaError> errors;
var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors); ProcessErrors(errors, sourcePath); return itemCollection;
}
}
return new EdmItemCollection();
} public string GetModelNamespace(string sourcePath)
{
ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath))
{
return string.Empty;
} var model = LoadRootElement(_host.ResolvePath(sourcePath));
if (model == null)
{
return string.Empty;
} var attribute = model.Attribute("Namespace");
return attribute != null ? attribute.Value : "";
} private bool ValidateInputPath(string sourcePath)
{
if (sourcePath == "$" + "edmxInputFile" + "$")
{
_errors.Add(
new CompilerError(_host.TemplateFile ?? sourcePath, , , string.Empty,
GetResourceString("Template_ReplaceVsItemTemplateToken")));
return false;
} return true;
} public XElement LoadRootElement(string sourcePath)
{
ArgumentNotNull(sourcePath, "sourcePath"); var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
return root.Elements()
.Where(e => e.Name.LocalName == "Runtime")
.Elements()
.Where(e => e.Name.LocalName == "ConceptualModels")
.Elements()
.Where(e => e.Name.LocalName == "Schema")
.FirstOrDefault()
?? root;
} private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath)
{
foreach (var error in errors)
{
_errors.Add(
new CompilerError(
error.SchemaLocation ?? sourceFilePath,
error.Line,
error.Column,
error.ErrorCode.ToString(CultureInfo.InvariantCulture),
error.Message)
{
IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning
});
}
} public bool IsLazyLoadingEnabled(EntityContainer container)
{
string lazyLoadingAttributeValue;
var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled";
bool isLazyLoading;
return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, out lazyLoadingAttributeValue)
|| !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading)
|| isLazyLoading;
}
} public static void ArgumentNotNull<T>(T arg, string name) where T : class
{
if (arg == null)
{
throw new ArgumentNullException(name);
}
} private static readonly Lazy<System.Resources.ResourceManager> ResourceManager =
new Lazy<System.Resources.ResourceManager>(
() => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true);
public static TypeMapper GlobaltypeMapper{get;set;}
public static CodeStringGenerator codeStringGenerator{get;set;}
public static CodeGenerationTools GlobalCode{get;set;}
public static string GetResourceString(string resourceName)
{
ArgumentNotNull(resourceName, "resourceName"); return ResourceManager.Value.GetString(resourceName, null);
}
#>

基础函数

 <#@ template language="C#" debug="True" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ output extension="cs" #>
<#@ include file="T4Toolbox.tt" #>
<#@ include file="EntityTemplate.tt" #>
<#@ output extension=".cs" encoding="UTF-8"#>
<# string curPath = Path.GetDirectoryName(Host.TemplateFile);
string destPath = Path.Combine(curPath, @"..\");
if(!Directory.Exists(destPath))
{
Directory.CreateDirectory(destPath);
}
const string inputFile = @"..\..\DataMould\SQLServer.edmx";
var textTransform = DynamicTextTransformation.Create(this);
var code = new CodeGenerationTools(this);
GlobalCode=new CodeGenerationTools(this);
var ef = new MetadataTools(this);
GlobaltypeMapper = new TypeMapper(code, ef, textTransform.Errors);
codeStringGenerator = new CodeStringGenerator(code, GlobaltypeMapper, ef);
var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
foreach (var entity in GlobaltypeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
EntityTemplate template = new EntityTemplate(entity);
string fileName = string.Format(@"{0}\{1}.cs", destPath, entity.Name);
template.Output.Encoding = Encoding.UTF8;
if(File.Exists(fileName))
{
File.Delete(fileName);
}
template.RenderToFile(fileName);
}
#>

代码生成

 <#@ template language="C#" debug="True" #>
<#@ output extension="cs" #>
<#@ include file="BaseClassTemplate.tt" #> <#+
// <copyright file="EntityTemplate.tt" company="">
// Copyright © . All Rights Reserved.
// </copyright> public class EntityTemplate : CSharpTemplate
{ private EntityType _entitis;
public EntityTemplate(EntityType Entitis)
{
_entitis = Entitis;
} public override string TransformText()
{
base.TransformText();
#>
using System;
using System.Collections.Generic;
namespace Entities
{
public class <#= _entitis.Name #>
{
<#+
var propertiesWithDefaultValues = GlobaltypeMapper.GetPropertiesWithDefaultValues(_entitis);
var collectionNavigationProperties = GlobaltypeMapper.GetCollectionNavigationProperties(_entitis);
var complexProperties = GlobaltypeMapper.GetComplexProperties(_entitis); if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())
{
#>
public <#=GlobalCode.Escape(_entitis)#>()
{
<#+
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=GlobalCode.Escape(edmProperty)#> = <#=GlobaltypeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
<#+
} foreach (var navigationProperty in collectionNavigationProperties)
{
#>
this.<#=GlobalCode.Escape(navigationProperty)#> = new HashSet<<#=GlobaltypeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
<#+
} foreach (var complexProperty in complexProperties)
{
#>
this.<#=GlobalCode.Escape(complexProperty)#> = new <#=GlobaltypeMapper.GetTypeName(complexProperty.TypeUsage)#>();
<#+
}
#>
}
<#+
}
var simpleProperties = GlobaltypeMapper.GetSimpleProperties(_entitis);
//var propertiesWithDefaultValues = GlobaltypeMapper.GetPropertiesWithDefaultValues(_entitis);
var navigationProperties = GlobaltypeMapper.GetNavigationProperties(_entitis); foreach(var property in simpleProperties)
{
#>
<#=codeStringGenerator.Property(property)#>
<#+
} if (navigationProperties.Any())
{
#> <#+
foreach (var navigationProperty in navigationProperties)
{
#>
<#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#+
}
} #>
}
}
<#+
return this.GenerationEnvironment.ToString();
}
}
#>

Entity生成模板

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

 <#@ template language="C#" debug="True" #>
<#@ output extension="cs" #>
<#@ include file="BaseClassTemplate.tt" #> <#+ public class EntityTemplate : CSharpTemplate
{ private EntityType _entitis;
public EntityTemplate(EntityType Entitis)
{
_entitis = Entitis;
} public override string TransformText()
{
base.TransformText();
#>
using System;
using System.Collections.Generic;
namespace DTO
{
public class <#= _entitis.Name.Replace("T_","")+"DTO" #>
{
<#+ var simpleProperties = GlobaltypeMapper.GetSimpleProperties(_entitis);
//var propertiesWithDefaultValues = GlobaltypeMapper.GetPropertiesWithDefaultValues(_entitis);
var navigationProperties = GlobaltypeMapper.GetNavigationProperties(_entitis);
var complexProperties = GlobaltypeMapper.GetComplexProperties(_entitis);
foreach(var property in simpleProperties)
{
#>
<#=codeStringGenerator.Property(property)#>
<#+
} #>
}
}
<#+
return this.GenerationEnvironment.ToString();
}
}
#>

DTO生成模板

  修改代码生成中的语句为

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

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

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

 void BeginTransaction();
void CommitTransaction();
void RollbackTransaction();

实现

 protected static DbEntities _dbEntities { get; set; }//我定义的dbcontext的基类
protected DbConnection _con { get; set; }
protected DbTransaction _tran { get; set; }
public BaseDao()
{
_dbEntities = new DbEntities();
}
public void BeginTransaction()
{
_con = ((IObjectContextAdapter)_dbEntities).ObjectContext.Connection;
_con.Open();
_tran=_con.BeginTransaction();
} public void CommitTransaction()
{
_tran.Commit();
_con.Close();
}
public void RollbackTransaction()
{
_tran.Rollback();
_con.Close();
}

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

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

以查询为例

        /// <summary>
/// 根据条件获取数据实体集合
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="exp">条件表达式</param>
/// <param name="useBaseContext">是否使用类基础的context,以达到延迟加载的目的</param>
/// <returns></returns>
public virtual IQueryable<T> GetEntities<T>(Expression<Func<T, bool>> exp, bool useBaseContext=false) where T : class
{
if (useBaseContext)
{
return _dbEntities.Set<T>().Where(exp);
}
using (DbEntities db = new DbEntities())
{
return db.Set<T>().Where(exp);
}
}

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

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

     public IEnumerable<UserDTO> GetList()
{ return (from a in _dal.GetEntities<T_User>(m=>m.T_UserID==, true)
select new UserDTO
{
T_UserID=a.T_UserID
}).ToList();
}

生成的语句

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

这里要注意,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的方式,应该怎样改造这个框架呢?