系列目录

前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多。主要集中增删该查每个模块都有,所以本次是为封装相同接口方法

   如果你想了解怎么重构普通的接口DAL层请查看第二节点

   如果你只想了解利用T4链接EF生成代码,可以忽略前两节,之后跳后最后T4模版的使用。

  (代码在最后)

   补充:

  最后必须让初学者理解一个知识点:分部类 partial 关键字,因为我们的重构是围绕分部类而实现,包括接口

partial 关键字指示可在命名空间中定义该类、结构或接口的其他部分。所有部分都必须使用 partial 关键字。在编译时,各个部分都必须可用来形成最终的类型。各个部分必须具有相同的可访问性,如 publicprivate 等。

如果将任意部分声明为抽象的,则整个类型都被视为抽象的。如果将任意部分声明为密封的,则整个类型都被视为密封的。如果任意部分声明基类型,则整个类型都将继承该类。

指定基类的所有部分必须一致,但忽略基类的部分仍继承该基类型。各个部分可以指定不同的基接口,最终类型将实现所有分部声明所列出的全部接口。在某一分部定义中声明的任何类、结构或接口成员可供所有其他部分使用。最终类型是所有部分在编译时的组合。

下列声明:

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

等效于下列声明:

class Earth : Planet, IRotate, IRevolve { }

知识点:

  1. 改变EF代码生成策略
    1. 改变生成策略为T4
    2. 添加TT模版
  2. 重构DAL层
    1. 创建ICommonRepository<T>接口
    2. 实现ICommonRepository<T>方法
  3. T4模版的使用

1.改变EF代码生成策略旧的ObjectContext改为T4(如果你的项目已经是DBContext API模式请跳过)(操作前备份项目)

在此之前我们也该改变一下代码生成策略,为什么会这个改变。这个项目我最初开始创建的时候用的EF版本为EF4.0当时EF4.0只提供了ObjectContext API接口模式

这个访问方式只能对于DataBase Frist用,不能用于Code Frist.所以一直用到这里,这个并不是很重要,最终都不会影响我们的功能开发

但是DBContext API的亮点,和现在教材都是基于DBContext策略的方式(EF4.1版本之后),我们有必要与时俱进。

操作方式很简单:

第一步:

从旧的ObjectContext改为T4(操作前备份项目)

第二步:

第三步:

->

最后看到DB.emdx下生成了很多模型类。根据表而生成的!这时代码编译将出错,根据出错的类库添加NUGET包。EntityFramework6.1.3版本,添加完之后还会继续报错。

因为ObjectContext有些方法和属性在DBContext已经不能用了

ObjectContext和ObjectSet都提供了AddObject的功能:

比如 context.AddObject("Students", newStudent):

context.Students.AddObject(newStudent):

而 DBContext中则只有DBSet有这个功能,并且名称为成了Add

context.Students.Add(newStudent):

也包括DeleteObject,ObjectStateManager等。

(忽略所有错误。这些错误将在我们重构DAL层被解决)

2.重构DAL层,下面我们来看一张图

图中绿色部分为本次重构部分,再利用成T4连接EF 生成通用分部类部分。这样我们下次不用手动创建继承类,只需要创建其他操作的分部类,很是简单。来看代码才明白

在第一节下载17讲代码

我们来看现有代码,以SysSample 模块的IDAL和DAL为例子

using Apps.Models;
using System.Linq;
namespace Apps.IDAL
{
public interface ISysSampleRepository
{
IQueryable<SysSample> GetList(DBContainer db);
int Create(SysSample entity);
int Delete(string id);
void Delete(DBContainer db, string[] deleteCollection);
/// <param name="entity">实体</param>
int Edit(SysSample entity);
SysSample GetById(string id);
bool IsExist(string id);
}
}
using System;
using System.Linq;
using Apps.IDAL;
using Apps.Models;
using System.Data; namespace Apps.DAL
{
public class SysSampleRepository : ISysSampleRepository, IDisposable
{
public IQueryable<SysSample> GetList(DBContainer db)
{
IQueryable<SysSample> list = db.SysSample.AsQueryable();
return list;
}
public int Create(SysSample entity)
{
using (DBContainer db = new DBContainer())
{
db.SysSample.AddObject(entity);
return db.SaveChanges();
}
}
public int Delete(string id)
{
using (DBContainer db = new DBContainer())
{
SysSample entity = db.SysSample.SingleOrDefault(a => a.Id == id);
if (entity != null)
{ db.SysSample.DeleteObject(entity);
}
return db.SaveChanges();
}
}
public int Delete(DBContainer db, string[] deleteCollection)
{
IQueryable<SysSample> collection = from f in db.SysSample
where deleteCollection.Contains(f.Id)
select f;
foreach (var deleteItem in collection)
{
db.SysSample.DeleteObject(deleteItem);
}
}
public int Edit(SysSample entity)
{
using (DBContainer db = new DBContainer())
{
db.SysSample.Attach(entity);
db.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
return db.SaveChanges();
}
}
public SysSample GetById(string id)
{
using (DBContainer db = new DBContainer())
{
return db.SysSample.SingleOrDefault(a => a.Id == id);
}
}
public bool IsExist(string id)
{
using (DBContainer db = new DBContainer())
{
SysSample entity = GetById(id);
if (entity != null)
return true;
return false;
}
}
public void Dispose()
{ }
}
}

看到我们其他模块也都必须实现类似代码。其中不同之处只有SysSample模型

好在.net提供索引访问对象的强类型List<T>这里的T代表SysSamle。是可变的

1.接下来我们实现通用接口在IDAL层

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks; namespace Apps.IDAL
{
public interface ICommonRepository<T> where T :class
{
bool Create(T model);
bool Edit(T model);
bool Delete(T model);
/// <summary>
/// 按主键删除
/// </summary>
/// <param name="keyValues"></param>
int Delete(params object[] keyValues);
T GetById(params object[] keyValues);
/// <summary>
/// 获得所有数据
/// </summary>
/// <returns></returns>
IQueryable<T> GetList();
/// <summary>
/// 根据表达式获取数据
/// </summary>
/// <param name="whereLambda"></param>
/// <returns></returns>
IQueryable<T> GetList(Func<T, bool> whereLambda);bool IsExist(string id);
int SaveChanges();
}
}

代码解析:接口实现了增删改查通用接口,并声明 where T :class 【T必须是一个类(class)类型】

2.接下来我们实现通用方法DAL层 按照重构图所示,应该继承ICommonRepository接口,并实现接口方法

using Apps.IDAL;
using Apps.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks; namespace Apps.DAL
{
public abstract class CommonRepository<T> : ICommonRepository<T> where T:class
{
DBContainer db;
public CommonRepository(DBContainer context)
{
this.db = context;
} public DBContainer Context
{
get { return db; }
} public virtual bool Create(T model)
{
db.Set<T>().Add(model);
return db.SaveChanges()>;
} public virtual bool Edit(T model)
{
if (db.Entry<T>(model).State == EntityState.Modified)
{
return db.SaveChanges() > ;
}
else if (db.Entry<T>(model).State == EntityState.Detached)
{
try
{
db.Set<T>().Attach(model);
db.Entry<T>(model).State = EntityState.Modified;
}
catch (InvalidOperationException)
{
//T old = Find(model._ID);
//db.Entry<old>.CurrentValues.SetValues(model);
}
return db.SaveChanges()>;
}
return false;
} public virtual bool Delete(T model)
{
db.Set<T>().Remove(model);
return db.SaveChanges()>;
} public virtual int Delete(params object[] keyValues)
{
T model = GetById(keyValues);
if(model!=null)
{
db.Set<T>().Remove(model);
return db.SaveChanges();
}
return -;
}
public virtual T GetById(params object[] keyValues)
{
return db.Set<T>().Find(keyValues);
} public virtual IQueryable<T> GetList()
{
return db.Set<T>();
} public virtual IQueryable<T> GetList(Func<T, bool> whereLambda)
{
return db.Set<T>().Where(whereLambda).AsQueryable();
}
public virtual bool IsExist(string id)
{
return GetById(id)!=null;
} public int SaveChanges()
{
return db.SaveChanges();
}
}
}

OK.全是DBContext API 操作EF方法。增删改查

3.T4生成分部类

接下来我们来看这么用这个接口

我们IDAL所有表(即模块)都要实现ICommonRepository接口好调用其中的增删改查做扩展

删掉ISysSampleRepository中的原来代码变为:

using Apps.Models;
using System.Linq;
namespace Apps.IDAL
{
public interface ISysSampleRepository : ICommonRepository<SysSample>
{ }
}

观察到这里也是重复代码,因为同理我们所有表都要实现这样的接口,这样我们可以利用T4来生成这种的重复代码

我以前写过一篇文章是关于T4链接数据库的。构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(29)-T4模版

这里我们是直接链接edmx的。同理

1.新建一个文件夹。如下图所示并创建一个TT模版

创建后会产生2个文件。删掉Context.tt,因为在Apps.Models已经有了

配置第五行的inputFile为上面所描述,可能因为环境不同你们EF路径有所不同。保存后TT模版会自动生成

tt模版如果没有高亮显示的。要安装一些工具。因为我是一边改一边发文章的。我这里也是没有的,下载T4Editor 安装高亮,什么版本就下什么就可以了(或者通过VS工具------扩展与更新 搜索)

http://t4-editor.tangible-engineering.com/Download_T4Editor_Plus_ModelingTools.html

tangible T4 Editor 2.3.0 plus modeling tools for Visual Studio 2015Download from Visual Studio Gallery

tangible T4 Editor 2.3.0 plus modeling tools for Visual Studio 2013Download from Visual Studio Gallery

按照ISysSampleRepository接口为例子。修改TT模版后代码

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF6.Utility.CS.ttinclude"#><#@
output extension=".cs"#><# const string inputFile = @"../../Apps.Models/DB.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("I"+entity.Name + "Repository.cs");
#>
using Apps.Models;
namespace Apps.IDAL
{
public partial interface I<#=entity.Name #>Repository:ICommonRepository<<#=entity.Name #>>
{ }
<#
EndNamespace(code);
} fileManager.Process(); #>
<#+ public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager)
{
fileManager.StartHeader();
#>
//------------------------------------------------------------------------------
// <auto-generated>
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#>
//
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#>
// <#=CodeGenerationTools.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_EF6"; 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 navProp)
{
var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType());
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)),
navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
_code.Escape(navProp),
_code.SpaceAfter(Accessibility.ForGetter(navProp)),
_code.SpaceAfter(Accessibility.ForSetter(navProp)));
} 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(" + TypeMapper.FixNamespaces(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 => TypeMapper.FixNamespaces(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 => TypeMapper.FixNamespaces(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} virtual 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 static string FixNamespaces(string typeName)
{
return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial.");
} 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);
} typeName = FixNamespaces(typeName); 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, CodeGenerationTools.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 static void ArgumentNotNull<T>(T arg, string name) where T : class
{
if (arg == null)
{
throw new ArgumentNullException(name);
}
}
#>

ICommonRepository

核心代码解析:

foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
fileManager.StartNewFile("I"+entity.Name + "Repository.cs");
#>
using Apps.Models;
namespace Apps.IDAL
{
public partial interface I<#=entity.Name #>Repository:ICommonRepository<<#=entity.Name #>>
{ }
<#
EndNamespace(code);
} fileManager.Process(); #>

循环生成。达到预期效果

TT模版里面太多代码很难看懂,需要看官方帮助文档才行。但是我们可以提取公共部分。以后写TT就引入。看图

在WriteHeder方法后都是通用的访问代码。我们提取之后的代码。因为安装了T4高亮。我只能调浅色,深色效果太差

在Apps.Models创建 Common.ttinclude来做公共部分(你可以创建在其他路径,到时候是引入路径为准)

最后修改:ICommonRepository.tt第二行代码:

<#@ include file="../../Apps.Models/Common.ttinclude"#><#@ 

OK。看起来间接很多了,以后创建其他tt文件就可以直接引用公共部分,如果不提取tt模版也是没有问题的。

同理我们创建DAL层的公共部分,生成后的代码如下,同样是一个分部类CommonRepository

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="../../Apps.Models/Common.ttinclude"#><#@
output extension=".cs"#>
<# const string inputFile = @"../../Apps.Models/DB.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("I"+entity.Name + "Repository.cs");
#>
using Apps.Models;
using Apps.DAL;
namespace Apps.IDAL
{
public partial class <#=entity.Name #>Repository:CommonRepository<<#=entity.Name #>>
{
public <#=entity.Name #>Repository(DBContainer db):base(db)
{ }
}
<#
EndNamespace(code);
} fileManager.Process(); #>

CommonRepository.tt

//------------------------------------------------------------------------------
// <auto-generated>
// 此代码已从模板生成。
//
// 手动更改此文件可能导致应用程序出现意外的行为。
// 如果重新生成代码,将覆盖对此文件的手动更改。
// </auto-generated>
//------------------------------------------------------------------------------ using Apps.Models;
using Apps.DAL;
namespace Apps.IDAL
{
public partial class SysSampleRepository:CommonRepository<SysSample>
{
public SysSampleRepository(DBContainer db):base(db)
{ }
}
}

接下来原来很长的ISysSampleRepository接口最后变成了一个分部接口

using Apps.Models;
using System.Linq;
namespace Apps.IDAL
{
public partial interface ISysSampleRepository
{
  //处理通用部分外的业务
}
}

SysSampleRepository变成了一个分部类

using System;
using System.Linq;
using Apps.IDAL;
using Apps.Models;
using System.Data; namespace Apps.DAL
{
public class SysSampleRepository
{
    //处理通用部分外的业务
} }

可以处理通用部分外的业务

这样我们关注点真的很专注了。Ye...

关于业务层,肯定还是报错的状态,这时候我们就来修改一下

例如:

变为

最后业务层代码:

//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由T4模板自动生成
// 生成时间 2013-04-22 10:41:47 by App
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.Unity;
using Apps.Models;
using Apps.Common;
using System.Transactions;
using Apps.Models.Sys;
using Apps.IBLL;
using Apps.IDAL;
using Apps.BLL.Core;
using Apps.Locale; namespace Apps.BLL
{
public class SysSampleBLL : BaseBLL, ISysSampleBLL
{ [Dependency]
public ISysSampleRepository m_Rep { get; set; } public List<SysSampleModel> GetList(ref GridPager pager, string queryStr)
{ IQueryable<SysSample> queryData = null;
if (!string.IsNullOrWhiteSpace(queryStr))
{
queryData = m_Rep.GetList(a => a.Name.Contains(queryStr) || a.Note.Contains(queryStr));
}
else
{
queryData = m_Rep.GetList();
}
pager.totalRows = queryData.Count();
//排序
queryData = LinqHelper.SortingAndPaging(queryData, pager.sort, pager.order, pager.page, pager.rows);
return CreateModelList(ref queryData);
}
private List<SysSampleModel> CreateModelList(ref IQueryable<SysSample> queryData)
{ List<SysSampleModel> modelList = (from r in queryData
select new SysSampleModel
{
Id = r.Id,
Name = r.Name,
Age = r.Age,
Bir = r.Bir,
Photo = r.Photo,
Note = r.Note,
CreateTime = r.CreateTime, }).ToList(); return modelList;
} public bool Create(ref ValidationErrors errors, SysSampleModel model)
{
try
{
SysSample entity = m_Rep.GetById(model.Id);
if (entity != null)
{
errors.Add(Resource.PrimaryRepeat);
return false;
}
entity = new SysSample();
entity.Id = model.Id;
entity.Name = model.Name;
entity.Age = model.Age;
entity.Bir = model.Bir;
entity.Photo = model.Photo;
entity.Note = model.Note;
entity.CreateTime = model.CreateTime; if (m_Rep.Create(entity))
{
return true;
}
else
{
errors.Add(Resource.InsertFail);
return false;
}
}
catch (Exception ex)
{
errors.Add(ex.Message);
ExceptionHander.WriteException(ex);
return false;
}
} public bool Delete(ref ValidationErrors errors, string id)
{
try
{
if (m_Rep.Delete(id) == )
{
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
errors.Add(ex.Message);
ExceptionHander.WriteException(ex);
return false;
}
} public bool Delete(ref ValidationErrors errors, string[] deleteCollection)
{
try
{
if (deleteCollection != null)
{
using (TransactionScope transactionScope = new TransactionScope())
{ m_Rep.Delete(deleteCollection);
if (db.SaveChanges() == deleteCollection.Length)
{
transactionScope.Complete();
return true;
}
else
{
Transaction.Current.Rollback();
return false;
}
}
}
return false;
}
catch (Exception ex)
{
errors.Add(ex.Message);
ExceptionHander.WriteException(ex);
return false;
}
} public bool Edit(ref ValidationErrors errors, SysSampleModel model)
{
try
{
SysSample entity = m_Rep.GetById(model.Id);
if (entity == null)
{
errors.Add(Resource.Disable);
return false;
}
entity.Name = model.Name;
entity.Age = model.Age;
entity.Bir = model.Bir;
entity.Photo = model.Photo;
entity.Note = model.Note; if (m_Rep.Edit(entity))
{
return true;
}
else
{
errors.Add(Resource.EditFail);
return false;
} }
catch (Exception ex)
{
errors.Add(ex.Message);
ExceptionHander.WriteException(ex);
return false;
}
} public bool IsExists(string id)
{
if (db.SysSample.SingleOrDefault(a => a.Id == id) != null)
{
return true;
}
return false;
} public SysSampleModel GetById(string id)
{
if (IsExist(id))
{
SysSample entity = m_Rep.GetById(id);
SysSampleModel model = new SysSampleModel();
model.Id = entity.Id;
model.Name = entity.Name;
model.Age = entity.Age;
model.Bir = entity.Bir;
model.Photo = entity.Photo;
model.Note = entity.Note;
model.CreateTime = entity.CreateTime; return model;
}
else
{
return null;
}
} public bool IsExist(string id)
{
return m_Rep.IsExist(id);
}
}
}

--------------------------------------------------------------------------------写在最后--------------------------------------------------------------------------------

可以对比以前DAL层的代码,明显的减少很多(虽然业务层没有减少)。也许在以后业务层也有必要的重构!

最后我完全修改了我项目的DAL层。用数据直接说话

整整少了两万行。却完成了相同的功能。(代码类型.cs,与事实可能有点差别,但是可以效果明显)

代码参考下载。重构后的架构(VS2013)执行根目录下的script.sql脚本。并修改web.config数据库链接即可查看

如查看重构前代码可以到第一节下载17节代码

https://yunpan.cn/cYUdjssbmiLrL  提取码 e622

ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构的更多相关文章

  1. ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(持续更新中...)

    开发工具:VS2015(2012以上)+SQL2008R2以上数据库  您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB  升级后界面效果如下: 任务调度系统界面 http: ...

  2. ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(转)

    开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 日程管理   http://ww ...

  3. ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出-自定义表模导入

    系列目录 前言 上一节使用了LinqToExcel和CloseXML对Excel表进行导入和导出的简单操作,大家可以跳转到上一节查看: ASP.NET MVC5+EF6+EasyUI 后台管理系统(6 ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统-WebApi的用法与调试

    1:ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-WebApi与Unity注入 使用Unity是为了使用我们后台的BLL和DAL层 2:ASP.NET MVC5+EF6+Easy ...

  5. ASP.NET MVC5+EF6+EasyUI 后台管理系统(51)-系统升级

    系统很久没有更新内容了,期待已久的更新在今天发布了,最近花了2个月的时间每天一点点,从原有系统 MVC4+EF5+UNITY2.X+Quartz 2.0+easyui 1.3.4无缝接入 MVC5+E ...

  6. ASP.NET MVC5+EF6+EasyUI 后台管理系统(34)-文章发布系统①-简要分析

    系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(1)-前言与,虽然有点没有目的的学习,但还是了解了Andro ...

  7. ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控

    系列目录 先补充一个平面化登陆页面代码,自己更换喜欢的颜色背景 @using Apps.Common; @{ Layout = null; } <!DOCTYPE html> <ht ...

  8. ASP.NET MVC5+EF6+EasyUI 后台管理系统(56)-插件---单文件上传与easyui使用fancybox

    系列目录 https://yunpan.cn/cZVeSJ33XSHKZ  访问密码 0fc2 今天整合lightbox插件Fancybox1.3.4,发现1.3.4版本太老了.而目前easyui 1 ...

  9. ASP.NET MVC5+EF6+EasyUI 后台管理系统(38)-Easyui-accordion+tree漂亮的菜单导航

    系列目录 本节主要知识点是easyui 的手风琴加树结构做菜单导航 有园友抱怨原来菜单非常难看,但是基于原有树形无限级别的设计,没有办法只能已树形展示 先来看原来的效果 改变后的效果,当然我已经做好了 ...

随机推荐

  1. 关于 Chrome 浏览器中 onresize 事件的 Bug

    我在写插件时用到了 onresize 事件,在反复地测试后发现该事件在 Chrome 及 Opera(内核基本与 Chrome 相同,以下统称 Chrome)浏览器打开时就会执行,这种情况也许不能算作 ...

  2. 菜鸟学Struts2——Interceptors

    昨天学习Struts2的Convention plugin,今天利用Convention plugin进行Interceptor学习,虽然是使用Convention plugin进行零配置开发,这只是 ...

  3. Bootstrap 模态框(Modal)插件

    页面效果: html+js: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  4. 开始学nodejs——net模块

    net模块的组成部分 详见 http://nodejs.cn/api/net.html 下面整理出了整个net模块的知识结构,和各个事件.方法.属性的用法 net.Server类 net.Socket ...

  5. Solr高级查询Facet

    一.什么是facet solr种以导航为目的的查询结果成为facet,在用户查询的结果上根据分类增加了count信息,然后用户根据count信息做进一步搜索. facet主要用于导航实现渐进式精确搜索 ...

  6. python 数据类型 --- 集合

    1. 注意列表和集合的区别 set 列表表现形式: list_1 = [1,3,4];  集合表现形式:set_1= set() list_1 = [1,2,3,4,23,4,2] print(lis ...

  7. 多线程条件通行工具——CountDownLatch

    CountDownLatch的作用是,线程进入等待后,需要计数器达到0才能通行. CountDownLatch(int)构造方法,指定初始计数. await()等待计数减至0. await(long, ...

  8. BPM配置故事之案例2-文本默认值

    Boss感觉方便了很多,然而采购部采购员阿海却还是有点意见,他跑来找小明. 阿海:现在申请都是我在提交,申请人和申请部门能不能不要每次都要填写啊,好麻烦的. 小明:没问题,这个简单. 小明在表单中把申 ...

  9. iOS中支付宝集成

    iOS中支付宝集成 如今各种的App中都使用了三方支付的功能,现在将我在使用支付宝支付集成过程的心得分享一下,希望对大家都能有所帮助 要集成一个支付宝支付过程的环境,大致需要: 1>公司:先与支 ...

  10. C语言可以开发哪些项目?

    C语言是我们大多数人的编程入门语言,对其也再熟悉不过了,不过很多初学者在学习的过程中难免会出现迷茫,比如:不知道C语言可以开发哪些项目,可以应用在哪些实际的开发中--,这些迷茫也导致了我们在学习的过程 ...