1.使用框架可以有效的解决耦合性过高的问题,减少代码修改的程度,同时方便添加新的功能。首先创建出基本的几个类库。这个框架就是使用基本的逻辑分层三层架构,然后进一步再使用接口对每个逻辑中的类库调用进行解耦。
8个基本的类库:DAL、IDAL、DALFactory、Common、Model、BLL、IBLL、WebApp
2.上层访问底层的时候,使用的是访问接口代替直接访问底层类,实现面向接口编程。下面使用一个基本的表Users表搭建基本的框架。
IDAL中因为有很多的公共的接口方法,比如说基本的增删改查,对于下面的每一个接口类(UserInfo、OrderInfo)中都存在这些接口,所以提出一个公共的接口IBaseDal<T>
namespace OA.IDAL { //这是所有的dal中公有的方法 public interface IBaseDal<T> where T:class,new() { //获取满足条件的实体列表 IQueryable<T> LoadEntity(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda); //获取满足条件的分页数据列表(排序) IQueryable<T> LoadPageEntity<S>(int pageSize, int pageIndex, out int totalCount, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderLambda, bool isAsc); //添加数据 T AddEntity(T entity); //修改数据 bool EditEntity(T entity); //删除数据 bool DeleteEntity(T entity); } }
对于参数T必须是一些Model中具体的类而不是接口,以为访问的接口中的方法就跟访问具体Dal中的方法是一样的,都是返回具体的查询的Model数据的。
3.此时的IUserInfoDal只需要继承自IBaseDal<UserInfo>,然后在IUserInfo中只需要定义单独针对这张表的具体逻辑方法就好了。接口只是规范,规定无论是什么样的数据库,操作类都必须实现这些定义的接口规范(方法)
public interface IUserDal:IBaseDal<Users> { //定义自己特有的方法 }
4.接下来就是让UserDal实现IUserDal中的方法,也就是实现具体的增删改查这几个基本的方法,但是问题来了,出了返回的Model不同,其他的操作逻辑完全一致,所有,提出一个公共类BaseDal<T>,这个类的作用就是实现方法的共用,省去了在每一个子类中书写一遍这几个基本的方法。
namespace OA.DAL { public class BaseDal<T> where T:class,new() { Model.OAEntities dbContext = new Model.OAEntities(); //记住添加引用EntityFarameWork public IQueryable<T> LoadEntity(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda) { return dbContext.Set<T>().Where<T>(whereLambda); } public IQueryable<T> LoadPageEntity<S>(int pageSize, int pageIndex, out int totalCount, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderLambda, bool isAsc) { //首先获取到totalCount IQueryable<T> totalEntities = dbContext.Set<T>().Where<T>(whereLambda); totalCount = totalEntities.Count(); if (isAsc) { totalEntities = totalEntities.OrderBy<T, S>(orderLambda).Skip<T>((pageIndex - 1) * pageSize).Take<T>(pageSize); } else { totalEntities = totalEntities.OrderByDescending<T, S>(orderLambda).Skip<T>((pageIndex - 1) * pageSize).Take<T>(pageSize); } return totalEntities; } public T AddEntity(T entity) { dbContext.Entry<T>(entity).State = EntityState.Added; dbContext.SaveChanges(); return entity; } public bool EditEntity(T entity) { dbContext.Entry<T>(entity).State = EntityState.Modified; return dbContext.SaveChanges() > 0; } public bool DeleteEntity(T entity ) { dbContext.Entry<T>(entity ).State = EntityState.Deleted; return dbContext.SaveChanges() > 0; } } }
上面的代码dbContext.Set<T>().Where<T>(whereLambda),这一句原来的代码是从dbContext.User.Where...修改过来的,因为不能使用DbContext.T.Where...,但是EntityFramework,使用Set<T>可以延迟获取到对应的内存表。同时上面的传递的参数都是Lambada表达式,因为对于数据的访问全部使用的是EntityFramework,它使用的就是Lambda表达式。
5.将UserDal继承自BaseDal<Users>,然后在继承自IUsersDal,就可以了,同时一定先继承BaseDal然后再继承IUsersDal
namespace OA.DAL { public class UserDal:BaseDal<Users>,IUserDal { } }
6.接下来在数据层和业务逻辑层添加一个数据访问层(DBSession),这个层封装了所有的数据访问层的Dal实例的创建,所以本质上来说,这个DBSession实际上就是一个工厂类,创建每一个实例,这种管理的方法实现了数据访问层图逻辑和业务逻辑城的解耦。它的主要的目的就是为了解决当操作多张表逻辑的时候,仅仅操作一次数据库,也就是实现工作单元模式。
namespace OA.DALFactory { //其实就是相当于一个工厂类,用来创建Dal的实例,同时实现工作单元模式 public class DBSession { Model.OAEntities DbContext = new Model.OAEntities(); //可以使用方法获取,也可以使用属性获取 private IUserDal _userDal; public IUserDal UserDal { get { if (_userDal == null) { _userDal = new UserDal(); } return _userDal; } set { _userDal = value; } } //实现工作单元模式 public bool SaveChanges() { return DbContext.SaveChanges()>0; } } }
7.工作单元模式已经实现了,那么在Dal中的所有的DBContext.SaveChanges()就需要去掉了
namespace OA.DAL { public class BaseDal<T> where T:class,new() { Model.OAEntities dbContext = new Model.OAEntities(); //记住添加引用EntityFarameWork public IQueryable<T> LoadEntity(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda) { return dbContext.Set<T>().Where<T>(whereLambda); } public IQueryable<T> LoadPageEntity<S>(int pageSize, int pageIndex, out int totalCount, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderLambda, bool isAsc) { //首先获取到totalCount IQueryable<T> totalEntities = dbContext.Set<T>().Where<T>(whereLambda); totalCount = totalEntities.Count(); if (isAsc) { totalEntities = totalEntities.OrderBy<T, S>(orderLambda).Skip<T>((pageIndex - 1) * pageSize).Take<T>(pageSize); } else { totalEntities = totalEntities.OrderByDescending<T, S>(orderLambda).Skip<T>((pageIndex - 1) * pageSize).Take<T>(pageSize); } return totalEntities; } public T AddEntity(T entity) { dbContext.Entry<T>(entity).State = EntityState.Added; //dbContext.SaveChanges(); return entity; } public bool EditEntity(T entity) { dbContext.Entry<T>(entity).State = EntityState.Modified; // return dbContext.SaveChanges() > 0; return true; } public bool DeleteEntity(T entity ) { dbContext.Entry<T>(entity ).State = EntityState.Deleted; //return dbContext.SaveChanges() > 0; return true; } } }
8.那么此时出现了一个问题,我们在创建的UserDal(代码已经写在Basedal中了)已经创建过OAEntity的上下文对象了,此时在DBSession中有创建了一个,就是不一致了,因此需要解决一致性的问题。此时需要线程内唯一,
既然在DBSession中需要new一个对象,在BaseDal中也是需要new一个对象,实际上就是一个工厂类,与其在每一各类中都写一遍保证线程内唯一的判断,不如直接使用工厂
1 namespace OA.DAL 2 { 3 public static class DBContextFactory 4 { 5 //工厂类的作用就是创建实例对象,可以包括相同的实例,也可以是不相同的实例,同时使用CallContext(这个对象跟HttpContext作用是一样的)实现线程内唯一对象 6 7 public Model.OAEntities CreateDBContext() 8 { 9 Model.OAEntities dbContext = (Model.OAEntities)CallContext.GetData("dbContext"); 10 if (dbContext == null) 11 { 12 dbContext = new Model.OAEntities(); 13 CallContext.SetData("dbContext", dbContext); 14 } 15 return dbContext; 16 } 17 } 18 }
这个工厂比较特殊,因为他是生产的是同一个对象,它可以随便找个类库存放,但是不能存放在DalFactory中,因为DalFactory中引用了DAL和IDAL这两个类库,如果写在DALFactory中,BaseDal需要使用dbContext,需要引用DAlFactory,存在相互引用的错误,所以直接把DBContextFactory,直接放在DAL下面
9.修改BaseDal和DBSession中对EF对象的获取方式,保证线程内的唯一
BaseDal修改: public class BaseDal<T> where T:class,new() { DbContext dbContext = DBContextFactory.CreateDBContext(); 。。。。。。。。。。 } DBSession修改: public class DBSession { public DbContext dbContext { get { return DBContextFactory.CreateDBContext(); } } 。。。。。。。 }
10.对于DBSession中需要获取到具体的Dal操作类的实例,但是全部使用new的方式创建的,造成耦合性太高,此时需要创建抽象工厂类,使用反射的机制创建具体的实力类,以后修改仅仅需要修改配置就好了。
namespace OA.DALFactory { public class AbstractFactory { public static readonly string AssemblyPath = ConfigurationManager.AppSettings["AssemblyPath"].ToString(); public static readonly string NameSpace = ConfigurationManager.AppSettings["NameSpace"].ToString(); public static IUserDal CerateUserDal() { string fullClassName = AssemblyPath + ".UserDal"; return CreateInstance(fullClassName ) as IUserDal ; } private static object CreateInstance(string fullClassName) { Assembly assmbly = Assembly.Load(AssemblyPath); return assmbly.CreateInstance(fullClassName); } } }
11.BLL层对DBSession访问,应该访问相应的接口,实现解耦合
namespace OA.IDAL { public class IDBSession { DbContext DbContext { get; } IUserDal UserDal{get;set;} bool SaveChanges(); } }
然后让DBSession实现IDBSession的接口
12.接下来是对业务层的搭建,同时存在很多的通用的代码,比如增删改查,同时需要创建出DBSession的实例,每一个子类中都需要创建一个DBSession ,所以在父类中创建。
namespace OA.BLL { public abstract class BaseService<T> { public IDBSession CurrentDBSession { get { return new DBSession(); } }//在子类中也使用 public IBaseDal<T> CurrentDal { get; set; } public abstract void SetCurrentDal(); public BaseService() { SetCurrentDal(); } IQueryable<T> LoadEntity(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda) { //return CurrentDBSession.UserDal.LoadEntity(whereLambda);并不确定T是什么 return CurrentDal.LoadEntity(whereLambda); } public IQueryable<T> LoadPageEntity<S>(int pageSize, int pageIndex, out int totalCount, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderLambda, bool isAsc) { return CurrentDal.LoadPageEntity<S>(pageSize, pageIndex, out totalCount, whereLambda, orderLambda, isAsc); } public T AddEntity(T entity) { CurrentDal.AddEntity(entity); CurrentDBSession.SaveChanges(); return entity; } public bool EditEntity(T entity) { CurrentDal.EditEntity(entity); return CurrentDBSession.SaveChanges(); } public bool DeleteEntity(T entity) { CurrentDal.DeleteEntity(entity); return CurrentDBSession.SaveChanges(); } } }
13.让具体业务子类继承BaseService
namespace OA.BLL { public class UserService:BaseService<Users> { public override void SetCurrentDal() { //在具体的业务子类中可以确定确定具体的是哪一个Dal CurrentDal = CurrentDBSession.UserDal; } } }
14.封装业务类的接口,对于每一个业务类中的接口,增删改查除了返回的类型不同,方法完全相同,为了避免重复,提取父类IBaseServie<T
namespace OA.IBLL { public interface IBaseService<T> { IDBSession CurrentDBSession { get; } IBaseDal<T> CurrentDal { get; set; } IQueryable<T> LoadEntity(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda); IQueryable<T> LoadPageEntity<S>(int pageSize, int pageIndex, out int totalCount, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderLambda, bool isAsc); T AddEntity(T entity); bool EditEntity(T entity); bool DeleteEntity(T entity); } }
15.创建具体的每一个业务子类的接口,以IUserService为例。
namespace OA.IBLL { public interface IUserService:IBaseService<Model.Users> { } }
16.创建具体的业务子类,以UserService为例
1 namespace OA.BLL 2 { 3 public class UserService:BaseService<Users>,IUserService 4 { 5 //并不会手动去调用这个方法,所以没必要写在接口里 6 public override void SetCurrentDal() 7 { 8 //在具体的业务子类中可以确定确定具体的是哪一个Dal 9 CurrentDal = CurrentDBSession.UserDal; 10 } 11 } 12 }
17.由于BaseService中虽然简化了创建DBSession的代码,但是逻辑上依然没有减少,每次都new(),然而对于同一个逻辑操作多次数据库,此时需要多次CurrentSession,每次调用都有new,此时逻辑有点问题,同时创建了多个,因为不是同一个CurrentSession
namespace OA.DALFactory { public class DBSessionFactory { public static IDBSession CreateDbSession() { IDBSession dbSession =(IDBSession) CallContext.GetData("dbSession"); if (dbSession == null) { dbSession = new DBSession(); CallContext.SetData("dbSession", dbSession); } return dbSession; } } } 同时将BaseService中获取DBSession方式 改为 public abstract class BaseService<T> where T:class,new() { // public IDBSession CurrentDBSession { get { return new DBSession(); } }//在子类中也使用 public IDBSession CurrentDBSession { get { return DBSessionFactory.CreateDbSession(); } }//在子类中也使用 。。。。。。 }
18.UI层就可以直接使用
IUserService user=new UserService();直接使用,没有必要再单独创建工厂,因为一般不会随意改动UI层和BLL层
搭建完毕。