系列概述:全系列会详细介绍抽象工厂三层的搭建,以及EF高级应用和 ASP.NET MVC3.0简单应用,应用到的技术有Ef、Lambda、Linq、Interface、T4等。 由于网上对涉及到的技术概念介绍很多,因此本项目中不再对基本概念加以叙述。
系列一概述:详细介绍如何使用Ef快速创建领域模型并生成数据库,适合EF零基础朋友学习。
一、创建项目解决方案
1.1 创建空解决方案
1.2 创建Domain程序集
二、创建ADO.NET实体数据模型
2.1 在刚创建Domain程序集中创建EF
2.2 选择空模型
2.3 Ef添加完成
三、创建实体对象
3.1 在edmx编辑器空白处点击鼠标右键创建实体对象
3.2 创建属性
3.3 实体对象创建完成
3.4 添加对应关系(一个用户可以对应多条日志信息)
3.5 实体对象关系添加完成
四、生成数据表
4.1 edmx 设计器空白处右键选择根据模型生成数据库
4.2 执行生成的sql语句
4.3 执行完成
五、查看数据库
EF很聪明会帮我们自动创建主外键和索引
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IBaseDao9 where T:class, 10 new ()//约束T类型必须可以实例化 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda ); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
此时基接口中的CRUD方法就定义完成。接下来我们需要使用T4模版生成所有的实体类接口并实现IBaseDao接口。
三、生成所有的实体类接口
3.1 添加名为IDaoExt 的T4文本模版
3.2 在模版中贴入以下代码,其中注释的地方需要根据各自的项目进行更改
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx";//指定edmx实体模型所在的路径
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cnblogs.Rdst.Domain;//引用Domain的命名空间
namespace Cnblogs.Rdst.IDAO //实体类接口所在的命名空间
{
<#
foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) //便利edmx模型中映射的实体对象
{#>
public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成实体对象接口
{
}
<#};#>
}
3.3 T4模版编辑完成后,Ctrl+s保存,提示是否运行,点击确认。此时就自动帮我们生成了所有的实体类接口,并实现了IBaseDao接口,相应的也具有了CRUD方法定义。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IBaseDao9 where T:class, 10 new ()//约束T类型必须可以实例化 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda ); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
此时基接口中的CRUD方法就定义完成。接下来我们需要使用T4模版生成所有的实体类接口并实现IBaseDao接口。
三、生成所有的实体类接口
3.1 添加名为IDaoExt 的T4文本模版
3.2 在模版中贴入以下代码,其中注释的地方需要根据各自的项目进行更改
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx";//指定edmx实体模型所在的路径
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cnblogs.Rdst.Domain;//引用Domain的命名空间
namespace Cnblogs.Rdst.IDAO //实体类接口所在的命名空间
{
<#
foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) //便利edmx模型中映射的实体对象
{#>
public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成实体对象接口
{
}
<#};#>
}
3.3 T4模版编辑完成后,Ctrl+s保存,提示是否运行,点击确认。此时就自动帮我们生成了所有的实体类接口,并实现了IBaseDao接口,相应的也具有了CRUD方法定义。
概要:1、实现EF上线文线程唯一,有效避免了脏数据问题。
2、实现IBaseDao中定义的CRUD方法
一、创建数据访问层程序集
1.1 在解决方案中创建Implements文件夹,以存放实现体部分的程序集
1.2 在Implements文件夹中创建Cnblogs.Rdst.Dao程序集
1.3 添加如下引用
二、创建ObjectContextFactory获取EF上下文
2.1 在Cnblogs.Rdst.Dao程序集中创建ObjectContextFactory类,用来获取EF上下文。
当数据库更换为Mysql或其他数据库时,在这个类中可以实现替换。
当网站访问量增大时,为避免EF产生的脏数据问题,我们使用System.Runtime.Remoting.Messaging 命名空间下的CallContext来解决线程内上下文唯一。
CallContex更多了解http://msdn.microsoft.com/zh-cn/library/system.runtime.remoting.messaging.callcontext(v=VS.80).aspx
2.2 在ObjectContextFactory类中定义一个静态方法,用于对EF上下文进行处理
1 using System; 2 using System.Collections.Generic; 3 using System.Data.Objects; 4 using System.Linq; 5 using System.Runtime.Remoting.Messaging; 6 using System.Text; 7 using Cnblogs.Rdst.Domain; 8 using Cnblogs.Rdst.IDAO; 9 10 namespace Cnblogs.Rdst.Dao 11 { 12 public class ObjectContextFactory 13 { 14 public static System.Data.Objects.ObjectContext GetCurrentObjectContext() 15 { 16 //从CallContext数据槽中获取EF上下文 17 ObjectContext objectContext = CallContext.GetData(typeof (ObjectContextFactory).FullName) as ObjectContext; 18 if (objectContext==null) 19 { 20 //如果CallContext数据槽中没有EF上下文,则创建EF上下文,并保存到CallContext数据槽中 21 objectContext = new ModelContainer();//当数据库替换为MySql等,只要在次出EF更换上下文即可。 22 CallContext.SetData(typeof(ObjectContextFactory).FullName,objectContext); 23 } 24 return objectContext; 25 } 26 } 27 }
三、创建BaseDao,并实现CRUD方法
3.1 创建BaseDao类,实现IBaseDao中定义方法,用于所有实体类继承此基类。
3.2 BaseDao类实现代码
EF应用中需要注意:1、增加和查询是不需要附加实体的,如果删除和更新不是从上下文获取的实体,就需要先附加,再进行状态更改。
2、处理查询,增删改都需要调用SaveChange()提交操作。
1 using System; 2 using System.Collections.Generic; 3 using System.Data.Objects; 4 using System.Linq; 5 using System.Text; 6 using Cnblogs.Rdst.IDAO; 7 8 9 namespace Cnblogs.Rdst.Dao 10 { 11 public class BaseDao12 where T:class, 13 new() 14 15 { 16 ObjectContext objectContext= ObjectContextFactory.GetCurrentObjectContext() as ObjectContext;//获取EF上下文 17 18 /// 19 /// 加载实体集合 20 /// 21 /// 22 /// 23 public virtual IQueryable LoadEntites(Func bool> whereLambda) 24 { 25 return objectContext.CreateObjectSet ().Where (whereLambda).AsQueryable (); 26 } 27 28 /// 29 /// 分页加载数据 30 /// 31 /// 过滤条件 32 /// 页码 33 /// 页大小 34 /// 总记录数 35 /// 36 public virtual IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize, out int totalCount) 37 { 38 var tmp= objectContext.CreateObjectSet ().Where (whereLambda); 39 totalCount = tmp.Count(); 40 41 return tmp.Skip (pageSize * (pageIndex - 1))//跳过行数,最终生成的sql语句是Top(n) 42 .Take (pageSize) //返回指定数量的行 43 .AsQueryable (); 44 } 45 46 /// 47 /// 添加实体 48 /// 49 /// 50 /// 返回更新后的实体 51 public virtual T AddEntity(T entity) 52 { 53 objectContext.CreateObjectSet ().AddObject(entity); 54 objectContext.SaveChanges(); 55 return entity; 56 } 57 58 /// 59 /// 更新实体 60 /// 61 /// 62 /// 返回更新后的实体 63 public virtual T UpdateEntity(T entity) 64 { 65 objectContext.CreateObjectSet ().Attach(entity); 66 objectContext.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Modified);//将附加的对象状态更改为修改 67 objectContext.SaveChanges(); 68 return entity; 69 } 70 71 /// 72 /// 删除实体 73 /// 74 /// 75 /// 76 public virtual bool DelEntity(T entity) 77 { 78 objectContext.CreateObjectSet ().Attach(entity); 79 objectContext.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Deleted);//将附加的实体状态更改为删除 80 if (objectContext.SaveChanges()>0) 81 { 82 return true;//删除成功 83 } 84 else 85 { 86 return false;//删除失败 87 } 88 } 89 90 /// 91 /// 根据条件删除对象 92 /// 93 /// 条件 94 /// 95 public virtual bool DelEntityByWhere(Func bool> whereLambda) 96 { 97 var tmp= objectContext.CreateObjectSet ().Where (whereLambda);//根据条件从数据库中获取对象集合 98 foreach (var entity in tmp) 99 { 100 objectContext.CreateObjectSet ().DeleteObject(entity);//标记对象为删除状态删除 101 } 102 if (objectContext.SaveChanges() > 0) 103 { 104 return true; 105 } 106 else 107 { 108 return false; 109 } 110 } 111 } 112 }
四、使用T4模版生成所有实体对象的实现
4.1 和系列二中的方法一样创建T4模版,生成所有的实体类继承自BaseDao并实现各自的接口
以下是T4模版中的代码,需要更改相应的EF edmx模型路径、引用命名空间等。
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#> <# CodeGenerationTools code = new CodeGenerationTools(this); MetadataLoader loader = new MetadataLoader(this); CodeRegion region = new CodeRegion(this, 1); MetadataTools ef = new MetadataTools(this); string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx"; EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile); string namespaceName = code.VsNamespaceSuggestion(); EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this); #> using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.IDAO; using Cnblogs.Rdst.Domain; namespace Cnblogs.Rdst.Dao { <# foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) {#> public partial class <#=entity.Name#>Dao:BaseDao<<#=entity.Name#>>,I<#=entity.Name#>Dao { } <#};#> }
4.2 T4模版编辑完成后,ctrl+s保存并运行,就生成了所有实体类的实现了
至此也就实现了数据访问层的增删改查以及分页查询。
菜鸟级三层框架(EF+MVC)项目实战之 系列二 对数据访问层的抽象下 将实现数据访问层对业务层的统一入口
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IBaseDao9 where T:class, 10 new ()//约束T类型必须可以实例化 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda ); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
此时基接口中的CRUD方法就定义完成。接下来我们需要使用T4模版生成所有的实体类接口并实现IBaseDao接口。
三、生成所有的实体类接口
3.1 添加名为IDaoExt 的T4文本模版
3.2 在模版中贴入以下代码,其中注释的地方需要根据各自的项目进行更改
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx";//指定edmx实体模型所在的路径
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cnblogs.Rdst.Domain;//引用Domain的命名空间
namespace Cnblogs.Rdst.IDAO //实体类接口所在的命名空间
{
<#
foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) //便利edmx模型中映射的实体对象
{#>
public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成实体对象接口
{
}
<#};#>
}
3.3 T4模版编辑完成后,Ctrl+s保存,提示是否运行,点击确认。此时就自动帮我们生成了所有的实体类接口,并实现了IBaseDao接口,相应的也具有了CRUD方法定义。
概述:1、对数据访问层进行封装
2、对业务层提供统一入口
3、线程内实例唯一
一、数据访问层封装抽象
1.1、在程序集Cnblogs.Rdst.IDAO中创建IDBSession接口,其主要目的是将所有实体类封装为属性。
1.2、IDBSession接口中定义SaveChange()方法,定义该方法的意义会在业务逻辑中介绍。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public partial interface IDBSession 9 { 10 int SaveChange();//用于在业务逻辑层对提交进行管理 11 } 12 }
1.3、创建名为IDBSessionExt的T4模版,实现自动生成IDBSession的部分接口,其中将所有实体类定义为接口属性,以实现对数据访问层的封装。
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#> <# CodeGenerationTools code = new CodeGenerationTools(this); MetadataLoader loader = new MetadataLoader(this); CodeRegion region = new CodeRegion(this, 1); MetadataTools ef = new MetadataTools(this); string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx"; EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile); string namespaceName = code.VsNamespaceSuggestion(); EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this); #> using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.Domain; namespace Cnblogs.Rdst.IDAO { public partial interface IDBSession { <#foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) {#> I<#=entity.Name#>Dao <#=entity.Name#>Dao { get; set; } <#};#> } }
1.4、T4模版编辑完成后,运行后生成的代码如下:
二、数据访问层统一入口抽象
在程序集Cnblogs.Rdst.IDAO中创建IDbSessionFactory接口,为业务逻辑层提供统一访问入口。
该接口中定义GetCurrentDBSession()方法,其作用是通过该接口方法获取需要的实体对象。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IDBSessionFactory 9 { 10 IDBSession GetCurrentDBSession(); 11 } 12 }
三、实现IDBSession
3.1、在Cnblogs.Rdst.Dao程序集中创建DBSession 部分类
3.2、在Cnblogs.Rdst.Dao程序集中创建名为DBSessionExt的T4模版,将所有实体类自动封装为属性,以下是T4模版中的代码
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#> <# CodeGenerationTools code = new CodeGenerationTools(this); MetadataLoader loader = new MetadataLoader(this); CodeRegion region = new CodeRegion(this, 1); MetadataTools ef = new MetadataTools(this); string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx"; EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile); string namespaceName = code.VsNamespaceSuggestion(); EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this); #> using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.IDAO; namespace Cnblogs.Rdst.Dao { public partial class DBSession : IDBSession { <#foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) {#> private I<#=entity.Name#>Dao _<#=entity.Name#>Dao; public I<#=entity.Name#>Dao <#=entity.Name#>Dao { get { if (_<#=entity.Name#>Dao == null) { _<#=entity.Name#>Dao = new <#=entity.Name#>Dao(); } return _<#=entity.Name#>Dao; } set { _<#=entity.Name#>Dao = value; } } <#}#> } }
3.3保存模版并运行,T4模版会自动将所有实体对象封装为属性。如下代码所示:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using Cnblogs.Rdst.IDAO; 6 7 namespace Cnblogs.Rdst.Dao 8 { 9 public partial class DBSession : IDBSession 10 { 11 12 private INoteInfoDao _NoteInfoDao; 13 public INoteInfoDao NoteInfoDao 14 { 15 get 16 { 17 if (_NoteInfoDao == null) 18 { 19 _NoteInfoDao = new NoteInfoDao(); 20 } 21 return _NoteInfoDao; 22 } 23 set { _NoteInfoDao = value; } 24 } 25 26 private IUserInfoDao _UserInfoDao; 27 public IUserInfoDao UserInfoDao 28 { 29 get 30 { 31 if (_UserInfoDao == null) 32 { 33 _UserInfoDao = new UserInfoDao(); 34 } 35 return _UserInfoDao; 36 } 37 set { _UserInfoDao = value; } 38 } 39 } 40 }
3.4、打开刚创建的DBSession类,实现IDBSession接口。并重新SaveChanges()方法,SaveChange()方法中调用了EF上下文中的SaveChange(),
其用途会在业务逻辑层进行详细说明。
以下是DBSession类中的实现代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.IDAO; using System.Data.Objects; namespace Cnblogs.Rdst.Dao { public partial class DBSession : IDBSession { private ObjectContext _efContext; //EF上下文 public ObjectContext EfContext { get { if (_efContext == null) { _efContext= ObjectContextFactory.GetCurrentObjectContext(); } return _efContext; } set { _efContext = value; } } public int SaveChange() { return EfContext.SaveChanges();//调用SaveChanges()方法提交操作 } } }
四、实现数据访问层统一入口
接下来现在我们需要在Cnblogs.Rdst.Dao程序集中实现IDbSessionFactory接口
创建DBSessionFactory类并实现IDbSessionFactory接口,为了避免资源浪费,也用到了CallContex实现了线程内实例唯一。
以下是DBsessionFactory类中的实现代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.IDAO; using System.Runtime.Remoting.Messaging; namespace Cnblogs.Rdst.Dao { public class DBSessionFactory:IDBSessionFactory { public IDBSession GetCurrentDBSession() { IDBSession dbSession = CallContext.GetData(typeof(DBSessionFactory).FullName) as DBSession; if (dbSession == null) { dbSession=new DBSession(); CallContext.SetData(typeof(DBSessionFactory).FullName, dbSession); } return dbSession; } } }
至此,我们就已经完成了对数据访问层的封装。
总结:1、方便了对数据库的替换数据库,只需要在ObjectContextFactory中进行替换相应的FE上下文。
2、当数据库中的表或字段有更新时,我们只需要重新运行一下相应T4模版,就可以实现与数据库保存一致。
接下来的系列三中会介绍业务逻辑层是如何调用数据访问层,稍后会进行更新。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IBaseDao9 where T:class, 10 new ()//约束T类型必须可以实例化 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda ); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
此时基接口中的CRUD方法就定义完成。接下来我们需要使用T4模版生成所有的实体类接口并实现IBaseDao接口。
三、生成所有的实体类接口
3.1 添加名为IDaoExt 的T4文本模版
3.2 在模版中贴入以下代码,其中注释的地方需要根据各自的项目进行更改
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx";//指定edmx实体模型所在的路径
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cnblogs.Rdst.Domain;//引用Domain的命名空间
namespace Cnblogs.Rdst.IDAO //实体类接口所在的命名空间
{
<#
foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) //便利edmx模型中映射的实体对象
{#>
public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成实体对象接口
{
}
<#};#>
}
3.3 T4模版编辑完成后,Ctrl+s保存,提示是否运行,点击确认。此时就自动帮我们生成了所有的实体类接口,并实现了IBaseDao接口,相应的也具有了CRUD方法定义。
概述:业务逻辑层封装相对数据访问层来说较为简单,我们分为以下几步:
1、抽象基接口定义CRUD方法
2、应用T4模版生成所有实体接口
3、接口实现
一、接口定义
1.1、创建名为Cnblogs.Rdst.IBLL的程序集,主要用于业务逻辑层接口定义
并引用Cnblogs.Rdst.Domain和System.Data.Entity。这里需要注意,只要是用到EF实体,就需要添加System.Data.Entity引用。
1.2、创建IBaseService接口定义CRUD方法
这里可以直接将IBaseDao中定义的方法拷贝过来。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IBLL 7 { 8 public interface IBaseService9 where T:class, 10 new () 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize, out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
1.3、创建名为IServiceExt的T4模版,用于自动生成所有实体对象的接口,并继承自IBaseService接口
以下是T4模版中的代码:
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#> <# CodeGenerationTools code = new CodeGenerationTools(this); MetadataLoader loader = new MetadataLoader(this); CodeRegion region = new CodeRegion(this, 1); MetadataTools ef = new MetadataTools(this); string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx"; EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile); string namespaceName = code.VsNamespaceSuggestion(); EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this); #> using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.Domain; namespace Cnblogs.Rdst.IBLL { <# foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) {#> public interface I<#=entity.Name#>Service : IBaseService<<#=entity.Name#>> { } <#};#> }
以下是T4模版运行后,自动为我们生成的代码:
至此,业务逻辑层的接口就定义完成了。
二、抽象出业务逻辑层的基类
2.1、创建名为Cnblogs.Rdst.BLL程序集,并添加DAO、Domain、IBLL、IDAO、System.Data.Entity程序集引用,如下图
2.2、接下来是我们的重点,创建名为BaseService基类。该基类中实现了对数据访问层的调用,也实现了CRUD
步骤: 1、先将IDBSessionFactory封装为属性,用于获取IDBSession
2、再将IDBSession封装为属性,用于获取EF上下文对象
3、定义IBaseDao类型的CurrentDao属性,用于属性获取具体的实体对象
4、定义抽象方法 SetCurrentDao(),用于子类设置实现,为CurrentDao属性赋具体的实体对象
以下是这部分代码实现:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using Cnblogs.Rdst.IDAO; 6 using Cnblogs.Rdst.Dao; 7 8 9 namespace Cnblogs.Rdst.BLL 10 { 11 public abstract class BaseService12 where T : class, 13 new() 14 { 15 //构造函数 16 public BaseService() 17 { 18 //调用SetCurrentDao()方法,要求子类必须实现 19 SetCurrentDao(); 20 } 21 22 //获取EF实体工厂 23 IDBSessionFactory _dbSessionFactory; 24 IDBSession _dbSession; 25 26 public IDBSessionFactory DbSessionFactory 27 { 28 get 29 { 30 if (_dbSessionFactory == null) 31 { 32 _dbSessionFactory = new DBSessionFactory(); 33 } 34 return _dbSessionFactory; 35 } 36 set { _dbSessionFactory = value; } 37 } 38 39 40 public IDBSession DbSession 41 { 42 get 43 { 44 if (_dbSession == null) 45 { 46 _dbSession = DbSessionFactory.GetCurrentDBSession();//通过数据访问层提供的工厂获取EF实体对象 47 } 48 return _dbSession; 49 } 50 set { _dbSession = value; } 51 } 52 //数据访问层基接口类型可以接收数据访问层的所有实体Dao 53 public IBaseDao CurrentDao { get; set; } 54 55 //该方法用于子类实现,其作用是设置相应的实体Dao 56 public abstract void SetCurrentDao();
//以下是CRUD实现
2.3、实现CRUD
有了EF上下文,我们就可以实现CRUD
在实现增加和更新方法是,我们这时调用DBSessin中封装的SaveChanges()方法进行提交,主要目的是实现业务层控制提交。
由于EF具有延迟加载特性,因此我们利用此特性,实现批量操作时,一次提交数据库,已提程序高性能。
因此我们这时需要将BaseDao中的增加和更新方法中的SaveChange()方法注视掉,在此我就不贴出此部分代码了。
以下是CRUD实现代码:
1 //以下是CRUD实现 2 3 public virtual IQueryableLoadEntites(Func bool> whereLambda) 4 { 5 return this.CurrentDao.LoadEntites(whereLambda); 6 } 7 8 9 public virtual IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize, out int totalCount) 10 { 11 return this.CurrentDao.LoadEntites(whereLambda, pageIndex, pageSize, out totalCount); 12 } 13 14 15 public virtual T AddEntity(T entity) 16 { 17 var tmp= this.CurrentDao.AddEntity(entity); 18 this.DbSession.SaveChange(); 19 return tmp; 20 } 21 22 23 public virtual T UpdateEntity(T entity) 24 { 25 var tmp= this.CurrentDao.UpdateEntity(entity); 26 this.DbSession.SaveChange(); 27 return tmp; 28 } 29 30 31 public virtual bool DelEntity(T entity) 32 { 33 return this.CurrentDao.DelEntity(entity); 34 } 35 36 37 public virtual bool DelEntityByWhere(Func bool> whereLambda) 38 { 39 return this.DelEntityByWhere(whereLambda); 40 } 41 } 42 }
至此,BaseService业务逻辑层基类就封装完成,接下来使用T4模版自动生成所有实体。
2.4、创建名为ServiceExt的T4模版
以下是模版中的代码:
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#> <# CodeGenerationTools code = new CodeGenerationTools(this); MetadataLoader loader = new MetadataLoader(this); CodeRegion region = new CodeRegion(this, 1); MetadataTools ef = new MetadataTools(this); string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx"; EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile); string namespaceName = code.VsNamespaceSuggestion(); EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this); #> using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cnblogs.Rdst.IBLL; using Cnblogs.Rdst.Domain; namespace Cnblogs.Rdst.BLL { <# foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) {#> public partial class <#=entity.Name#>Service : BaseService<<#=entity.Name#>>, I<#=entity.Name#>Service { public override void SetCurrentDao() { this.CurrentDao = this.DbSession.<#=entity.Name#>Dao; } } <#};#> }
以下是运行T4模版后自动生成的代码:
这时我们就对业务逻辑层封装完成,系列四中使用MVC3.0简单实现增删改查以及分页功能。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IBaseDao9 where T:class, 10 new ()//约束T类型必须可以实例化 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda ); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
此时基接口中的CRUD方法就定义完成。接下来我们需要使用T4模版生成所有的实体类接口并实现IBaseDao接口。
三、生成所有的实体类接口
3.1 添加名为IDaoExt 的T4文本模版
3.2 在模版中贴入以下代码,其中注释的地方需要根据各自的项目进行更改
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx";//指定edmx实体模型所在的路径
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cnblogs.Rdst.Domain;//引用Domain的命名空间
namespace Cnblogs.Rdst.IDAO //实体类接口所在的命名空间
{
<#
foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) //便利edmx模型中映射的实体对象
{#>
public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成实体对象接口
{
}
<#};#>
}
3.3 T4模版编辑完成后,Ctrl+s保存,提示是否运行,点击确认。此时就自动帮我们生成了所有的实体类接口,并实现了IBaseDao接口,相应的也具有了CRUD方法定义。
概述:该节主要目的是通过ASP.Net MVC3.0快速实现增删改查。
一、创建MVC项目
1.1、在解决方案中添加MVC3.0项目
vs2010中没有自带MVC3.0,下载:http://www.asp.net/mvc/mvc3
在这里我们选择“空”模版
1.2、添加相关引用
1)由于使用的EF因此需要添加一坨相关引用,我们可以在MVC项目中的Model中添加一个ASP.NET 实体数据模型。此时VS会帮我们引用相应的程序集,完成后我再
删除它。
2) 再引用我们的程序集,Cnblogs.Rdst.IBLL、Cnblogs.Rdst.BLL、Cnblogs.Rdst.Domain
3、配置连接字符串
将Cnblogs.Rdst.Domain中的App.Config文件里的
<connectionStrings> <add name="ModelContainer" connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=Cnblogs-Rdst;persist security info=True;user id=sa;password=Hell0F18;multipleactiveresultsets=True;App=EntityFramework"" providerName="System.Data.EntityClient" /> connectionStrings>
拷贝到MVC项目中的web.config里的<configuration>节点下,如下图:
二、添加UserInfo控制器
2.1、在创建控制器前我们需要将解决方案编译一下。
2.2、回到MVC项目中右键点击Controllers,创建一个名为UserInfo的控制器
2.3、“模型类”选择UserInfo实体类,“数据上下文”选择ModelContainer实体上下文,在此我们选择Razor实体引擎。
三、快速实现CRUD
3.1 此时VS会帮我们生成UserInfo和View下的一些页面
3.2 以下代码是UserInfoController控制器中增删改查的代码,其中注释掉的代码是vs自动生成的。
1 using System; 2 using System.Collections.Generic; 3 using System.Data; 4 using System.Data.Entity; 5 using System.Linq; 6 using System.Web; 7 using System.Web.Mvc; 8 using Cnblogs.Rdst.Domain; 9 using Cnblogs.Rdst.IBLL; 10 using Cnblogs.Rdst.BLL; 11 12 namespace Cnblogs.Rdst.Mvc.Controllers 13 { 14 public class UserInfoController : Controller 15 { 16 //private ModelContainer db = new ModelContainer(); 17 IUserInfoService db = new UserInfoService(); 18 19 // 20 // GET: /UserInfo/ 21 22 public ViewResult Index() 23 { 24 // return View(db.UserInfo.ToList()); 25 return View(db.LoadEntites(u => true)); 26 } 27 28 // 29 // GET: /UserInfo/Details/5 30 31 public ViewResult Details(int id) 32 { 33 //UserInfo userinfo = db.UserInfo.Single(u => u.ID == id); 34 UserInfo userinfo = db.LoadEntites(u => u.ID == id).FirstOrDefault();//FirstOrDefault()获取集合第一项 35 return View(userinfo); 36 } 37 38 // 39 // GET: /UserInfo/Create 40 41 public ActionResult Create() 42 { 43 return View(); 44 } 45 46 // 47 // POST: /UserInfo/Create 48 49 [HttpPost] 50 public ActionResult Create(UserInfo userinfo) 51 { 52 if (ModelState.IsValid) 53 { 54 //db.UserInfo.AddObject(userinfo); 55 //db.SaveChanges(); 56 db.AddEntity(userinfo); 57 return RedirectToAction("Index"); 58 } 59 60 return View(userinfo); 61 } 62 63 // 64 // GET: /UserInfo/Edit/5 65 66 public ActionResult Edit(int id) 67 { 68 //UserInfo userinfo = db.UserInfo.Single(u => u.ID == id); 69 UserInfo userinfo = db.LoadEntites(u=>u.ID==id).FirstOrDefault(); 70 return View(userinfo); 71 } 72 73 // 74 // POST: /UserInfo/Edit/5 75 76 [HttpPost] 77 public ActionResult Edit(UserInfo userinfo) 78 { 79 if (ModelState.IsValid) 80 { 81 // db.UserInfo.Attach(userinfo); 82 // db.ObjectStateManager.ChangeObjectState(userinfo, EntityState.Modified); 83 // db.SaveChanges(); 84 db.UpdateEntity(userinfo); 85 return RedirectToAction("Index"); 86 } 87 return View(userinfo); 88 } 89 90 // 91 // GET: /UserInfo/Delete/5 92 93 public ActionResult Delete(int id) 94 { 95 // UserInfo userinfo = db.UserInfo.Single(u => u.ID == id); 96 UserInfo userinfo = db.LoadEntites(u => u.ID == id).FirstOrDefault(); 97 return View(userinfo); 98 } 99 100 // 101 // POST: /UserInfo/Delete/5 102 103 [HttpPost, ActionName("Delete")] 104 public ActionResult DeleteConfirmed(int id) 105 { 106 //UserInfo userinfo = db.UserInfo.Single(u => u.ID == id); 107 // db.UserInfo.DeleteObject(userinfo); 108 // db.SaveChanges(); 109 var userInfo=db.LoadEntites(u=>u.ID==id).FirstOrDefault(); 110 db.DelEntity(userInfo); 111 return RedirectToAction("Index"); 112 } 113 114 //protected override void Dispose(bool disposing) 115 //{ 116 // db.Dispose(); 117 // base.Dispose(disposing); 118 //} 119 } 120 }
3.3、接下来我们在Global.asax文件中配置一下路由,将controller = "Home"更改为controller = "UserInfo"
3.4、将Cnblogs.Rdst.Mvc项目设置为启动项,在点击启动调试。
在此由于涉及到表外键关系,我们需要将映射文件中的外键改为可空,在网页上才可以进行新增操作。
至此,面向对象、面向接口 入门级三层架构项目就已结束了,希望对大家有所帮助!
源码下载
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Cnblogs.Rdst.IDAO 7 { 8 public interface IBaseDao9 where T:class, 10 new ()//约束T类型必须可以实例化 11 { 12 //根据条件获取实体对象集合 13 IQueryable LoadEntites(Func bool> whereLambda ); 14 15 //根据条件获取实体对象集合分页 16 IQueryable LoadEntites(Func bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 17 18 //增加 19 T AddEntity(T entity); 20 21 //更新 22 T UpdateEntity(T entity); 23 24 //删除 25 bool DelEntity(T entity); 26 27 //根据条件删除 28 bool DelEntityByWhere(Func bool> whereLambda); 29 } 30 }
此时基接口中的CRUD方法就定义完成。接下来我们需要使用T4模版生成所有的实体类接口并实现IBaseDao接口。
三、生成所有的实体类接口
3.1 添加名为IDaoExt 的T4文本模版
3.2 在模版中贴入以下代码,其中注释的地方需要根据各自的项目进行更改
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = @"..\\Cnblogs.Rdst.Domain\\Model.edmx";//指定edmx实体模型所在的路径
EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cnblogs.Rdst.Domain;//引用Domain的命名空间
namespace Cnblogs.Rdst.IDAO //实体类接口所在的命名空间
{
<#
foreach (EntityType entity in ItemCollection.GetItems().OrderBy(e => e.Name)) //便利edmx模型中映射的实体对象
{#>
public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成实体对象接口
{
}
<#};#>
}
3.3 T4模版编辑完成后,Ctrl+s保存,提示是否运行,点击确认。此时就自动帮我们生成了所有的实体类接口,并实现了IBaseDao接口,相应的也具有了CRUD方法定义。