ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多。主要集中增删该查每个模块都有,所以本次是为封装相同接口方法
如果你想了解怎么重构普通的接口DAL层请查看第二节点
如果你只想了解利用T4链接EF生成代码,可以忽略前两节,之后跳后最后T4模版的使用。
(代码在最后)
补充:
最后必须让初学者理解一个知识点:分部类 partial 关键字,因为我们的重构是围绕分部类而实现,包括接口
partial 关键字指示可在命名空间中定义该类、结构或接口的其他部分。所有部分都必须使用 partial 关键字。在编译时,各个部分都必须可用来形成最终的类型。各个部分必须具有相同的可访问性,如 public、private 等。
如果将任意部分声明为抽象的,则整个类型都被视为抽象的。如果将任意部分声明为密封的,则整个类型都被视为密封的。如果任意部分声明基类型,则整个类型都将继承该类。
指定基类的所有部分必须一致,但忽略基类的部分仍继承该基类型。各个部分可以指定不同的基接口,最终类型将实现所有分部声明所列出的全部接口。在某一分部定义中声明的任何类、结构或接口成员可供所有其他部分使用。最终类型是所有部分在编译时的组合。
下列声明:
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
等效于下列声明:
class Earth : Planet, IRotate, IRevolve { }
知识点:
- 改变EF代码生成策略
- 改变生成策略为T4
- 添加TT模版
- 重构DAL层
- 创建ICommonRepository<T>接口
- 实现ICommonRepository<T>方法
- 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层重构的更多相关文章
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(持续更新中...)
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 任务调度系统界面 http: ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(转)
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 日程管理 http://ww ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出-自定义表模导入
系列目录 前言 上一节使用了LinqToExcel和CloseXML对Excel表进行导入和导出的简单操作,大家可以跳转到上一节查看: ASP.NET MVC5+EF6+EasyUI 后台管理系统(6 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统-WebApi的用法与调试
1:ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-WebApi与Unity注入 使用Unity是为了使用我们后台的BLL和DAL层 2:ASP.NET MVC5+EF6+Easy ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(51)-系统升级
系统很久没有更新内容了,期待已久的更新在今天发布了,最近花了2个月的时间每天一点点,从原有系统 MVC4+EF5+UNITY2.X+Quartz 2.0+easyui 1.3.4无缝接入 MVC5+E ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(34)-文章发布系统①-简要分析
系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(1)-前言与,虽然有点没有目的的学习,但还是了解了Andro ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控
系列目录 先补充一个平面化登陆页面代码,自己更换喜欢的颜色背景 @using Apps.Common; @{ Layout = null; } <!DOCTYPE html> <ht ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(56)-插件---单文件上传与easyui使用fancybox
系列目录 https://yunpan.cn/cZVeSJ33XSHKZ 访问密码 0fc2 今天整合lightbox插件Fancybox1.3.4,发现1.3.4版本太老了.而目前easyui 1 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(38)-Easyui-accordion+tree漂亮的菜单导航
系列目录 本节主要知识点是easyui 的手风琴加树结构做菜单导航 有园友抱怨原来菜单非常难看,但是基于原有树形无限级别的设计,没有办法只能已树形展示 先来看原来的效果 改变后的效果,当然我已经做好了 ...
随机推荐
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- PHP源码分析-变量
1. 变量的三要素变量名称,变量类型,变量值 那么在PHP用户态下变量类型都有哪些,如下: // Zend/zend.h #define IS_NULL 0 #define IS_LONG 1 #de ...
- .net windows Kafka 安装与使用入门(入门笔记)
完整解决方案请参考: Setting Up and Running Apache Kafka on Windows OS 在环境搭建过程中遇到两个问题,在这里先列出来,以方便查询: 1. \Jav ...
- 初步认识TDD
TDD,测试驱动开发(Test Driven Development)是极限编程中倡导的程序开发方法,以其倡导先写测试程序,然后编码实现其功能得名.本文将对TDD有一个较为系统的认识. 基础属性 ...
- Kooboo CMS技术文档之四:Kooboo CMS的站点组成部分
Kooboo CMS本着功能独立分离的原则,将站点分为三部分组成:用户管理,站点管理和内容数据库管理.各个功能之间既可独立使用,也可以容易组成在一起形成一个完整的系统. 用户管理 管理整个系统内的用户 ...
- VS2015在创建项目时的一些注意事项
一.下面是在创建一个新的项目是我最常用的,现在对他们一一做一个详细的介绍: 1.Win32控制台应用程序我平时编写小的C/C++程序都用它,它应该是用的最多的. 2.名称和解决方案名称的区别:名称是项 ...
- from表单提交数据之后,后台对象接受不到值
如果SSH框架下,前段页面通过from表单提交数据之后,在后台对象显示空值,也就是接收不到值得情况下.首先保证前段输入框有值,这个可以在提交的时候用jQuery的id或者name选择器alert弹出测 ...
- PHP 设计模式概述
一.设计模式(Design pattern)是什么? 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. ...
- wireshark 相关提示
Packet size limited during capture 提示说明标记的包没有抓全,在某些操作系统中,默认只抓96个字节,tcpdump中有"-s"参数可用于 ...
- 算是休息了这么长时间吧!准备学习下python文本处理了,哪位大大有好书推荐的说下!
算是休息了这么长时间吧!准备学习下python文本处理了,哪位大大有好书推荐的说下!