ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) (1):框架搭建 (2):数据库访问层的设计Demo (3):面向接口编程
前言:前面几篇博客我们基本已经介绍完了搭建整个项目和数据库访问层以及一些业务逻辑层的实现,当然了,我们的数据库访问层这样还是可以在进行封装的,但是我到这里就行了吧,项目也不大,不需要那么麻烦的,那么我们今天开始介绍我们需要介绍的内容,那就是我们对业务逻辑的封装,这个博客如果大家要看的话,我建议从第一张开始看是最容易理解的,废话不多说了,我们直接切入主题。
1. 初步设计业务逻辑层
(1) 从昨天我们对LYZJ.UserLimitMVC.BLL类库的操作,我们在这个类库下面添加了UserInfoService,那么其他的实体对象也是有Service,那么这时候又是重复性的动作,UserInfo含有操作数据库的所有方法,Role也含有操作数据库的方法,其他的表一样,那么我们怎么办呢,当然就是封装了,这时候我们就需要建立一个基类来实现这个动作。
(2) 首先我们在昨天的基础上面添加两个类,我们在LYZJ.UserLimitMVC.BLL类库下面再添加一个RoleService类,BaseService类。
2.基类BaseService的分析
(1)首先BaseService类肯定是一个泛型,限制其继承自类,这里面就会实现相应的增删改查的方法。
(2)那么这时候我先将BaseService的一部分代码帖上来,然后进行讲解,想必如果开发过这类项目的人都知道这块的使用,但是下面我还是解释一下,解释的不太好,完全是自己的语言,如果谁会解释的更好,希望能够给我留言,我将非常感谢。
1 namespace LYZJ.UserLimitMVC.BLL 2 { 3 public abstract class BaseService<T> where T : class, new() 4 { 5 public IDAL.IBaseRepository<T> CurrentRepository { get; set; } 6 //基类的构造函数 7 public BaseService() 8 { 9 SetCurrentRepository(); //构造函数里面去调用了,此设置当前仓储的抽象方法 10 } 11 12 //约束 13 public abstract void SetCurrentRepository(); //子类必须实现 14 15 public T AddEntity(T entity) 16 { 17 //调用T对应的仓储来做添加工作 18 return CurrentRepository.AddEntity(entity); 19 } 20 } 21 }
(3)下面就是我对上面代码的分析
首先写了方法Public T AddEntity(){return CurrentRepository.AddEntity(entity);},那么当我们对实体进行添加的时候,我们首先需要调用T对应的数据库访问层来调用相应的Entity方法,我们在昨天写UserInfoService的时候,我们是直接调用了UserInfoRepository(仓储)的Entity方法,但是现在我们在UserInfoService中的代码都要放到基类中去实现,那么基类中的T对应的是那个数据库访问层的仓储我们是不知道的,那么我们怎么办呢?
不要慌,我们仔细分析代码的话我们会发现解决之法,现在T对应的仓储是谁呢?我们需要在方法里面调用来实现添加工作,现在首先假设我们知道我们T对应的仓储叫CurrentRepository,我们只需要将entity参数传递给方法即可CurrentRepository.AddEntity(entity);,但是假设是假设,还是不成立的,但是这时候这就是我们的思想,我们已经知道这样可以解决,所以我们只需要找到解决的办法即可。
当前在我们基类中T对应的仓储是谁我们不知道,这里可以使用反射来实现,但是我这里就不这么做了,既然我们基类不知道T对应的仓储是谁?那么我们子类肯定T对应的仓储是谁,因为子类对应的就是实体,所以这时候我们有办法了,这时候我们只需要让子类来设置这个属性的实例不就完事了吗,这时候我们在基类下面写入一个属性,注意:我们当前仓储可能针对的是所有的表的仓储,那么我们这样写:public abstract void SetCurrentRepository();,那麽下面我们调用接口的属性就完事了。
这时候我们就想这里我们为什么要这么写呢?因为当前仓储可能指向任何一个仓储,有可能指向UserInfo仓储,也有可能指向RoleInfo仓储等,那么这里我们使用IDAL.IBaseRepositroy完全可以,因为当你仔细看我花的那张图的时候就看到了仓储基本都间接地继承自IBaseRepository仓储,那麽我们使用IBase仓储接收当前的仓储是没有任何问题的。
关键当我们这么定义的时候CurrentRepository是null,这时候我们下面方法如果调用的话可能会报出异常,那么在调用这个方法之前我们必须为CurrentRepository属性赋值,那麽给这个属性赋值在哪里比较合适呢,我想到了构造方法,构造方法给这个属性赋值最合适,在这当前我们基类中也不知道CurrentRepository赋什么值,只有子类中才知道,这时候父类就想让子类给这个属性赋值,那麽怎么办呢?我们定义一个抽象方法,当我们定义抽象方法的时候我们的类也要改成抽象的,抽象方法如下:public abstract void SetCurrentRepository(); ,这个抽象方法子类必须实现,在这里我在构造函数里面再去调用这个抽象方法。
在基类里面的构造函数调用父类的纯的抽象方法,那麽经过多态的话最终调用方法的时候去执行子类里面的仓储。
到这里我关于业务逻辑层的基类解释已经完成了,个人感觉语言真心组织的不好,如果哪位看不懂或者怎么的,可以加群,我们在共同探讨一下,到这里我们的LYZJ.UserLimitMVC.BLL类库完成了
3. LYZJ.UserLimitMVC.BLL类库的源代码
(1)因为这个类库的实现我已经在上面讲玩了,所以这里不累赘的再说了,下面我直接贴最后的代码,大家可以参考一下。
(2)BaseService.CS
1 namespace LYZJ.UserLimitMVC.BLL 2 3 { 4 5 public abstract class BaseService<T> where T : class, new() 6 7 { 8 9 public IDAL.IBaseRepository<T> CurrentRepository { get; set; } 10 11 //基类的构造函数 12 13 public BaseService() 14 15 { 16 17 SetCurrentRepository(); //构造函数里面去调用了,此设置当前仓储的抽象方法 18 19 } 23 public abstract void SetCurrentRepository(); //子类必须实现 24 27 //实现对数据库的添加功能 28 29 public T AddEntity(T entity) 30 31 { 32 33 //调用T对应的仓储来做添加工作 34 35 return CurrentRepository.AddEntity(entity); 36 37 } 41 //实现对数据的修改功能 42 43 public bool UpdateEntity(T entity) 44 45 { 46 47 return CurrentRepository.UpdateEntity(entity); 48 49 } 53 //实现对数据库的删除功能 54 55 public bool DeleteEntity(T entity) 56 57 { 58 59 return CurrentRepository.DeleteEntity(entity); 60 61 } 65 //实现对数据库的查询 --简单查询 66 67 public IQueryable<T> LoadEntities(Func<T, bool> whereLambda) 68 69 { 70 71 return CurrentRepository.LoadEntities(whereLambda); 72 73 } 77 /// <summary> 78 79 /// 实现对数据的分页查询 80 81 /// </summary> 82 83 /// <typeparam name="S">按照某个类进行排序</typeparam> 84 85 /// <param name="pageIndex">当前第几页</param> 86 87 /// <param name="pageSize">一页显示多少条数据</param> 88 89 /// <param name="total">总条数</param> 90 91 /// <param name="whereLambda">取得排序的条件</param> 92 93 /// <param name="isAsc">如何排序,根据倒叙还是升序</param> 94 95 /// <param name="orderByLambda">根据那个字段进行排序</param> 96 97 /// <returns></returns> 98 99 public IQueryable<T> LoadPageEntities<S>(int pageIndex, int pageSize, out int total, Func<T, bool> whereLambda, 100 101 bool isAsc, Func<T, S> orderByLambda) 102 103 { 104 105 return CurrentRepository.LoadPageEntities(pageIndex, pageSize, out total, whereLambda, isAsc, orderByLambda); 106 107 } 108 109 } 110 111 }
(3)UserInfoService.cs
1 namespace LYZJ.UserLimitMVC.BLL 2 3 { 4 5 /// <summary> 6 7 /// UserInfo业务逻辑 8 9 /// </summary> 10 11 public class UserInfoService:BaseService<UserInfo> 12 13 { 14 15 public override void SetCurrentRepository() 16 17 { 18 19 CurrentRepository = DAL.RepositoryFactory.UserInfoRepository; 20 21 } 22 23 //访问DAL实现CRUD 24 25 //private DAL.UserInfoRepository _userInfoRepository = new UserInfoRepository(); 26 27 //依赖接口编程 28 29 //private IUserInfoRepository _userInfoRepository = new UserInfoRepository(); 30 31 //private IUserInfoRepository _userInfoRepository = RepositoryFactory.UserInfoRepository; 32 33 //public UserInfo AddUserInfo(UserInfo userInfo) 34 35 //{ 36 37 // return _userInfoRepository.AddEntity(userInfo); 38 39 //} 40 41 //public bool UpdateUserInfo(UserInfo userInfo) 42 43 //{ 44 45 // return _userInfoRepository.UpdateEntity(userInfo); 46 47 //} 48 49 } 50 51 }
(4)RoleService.cs
1 namespace LYZJ.UserLimitMVC.BLL 2 3 { 4 5 public class RoleService:BaseService<Role> 6 7 { 8 9 //重写抽象方法,设置当前仓储为Role仓储 10 11 public override void SetCurrentRepository() 12 13 { 14 15 //设置当前仓储为Role仓储 16 17 CurrentRepository = DAL.RepositoryFactory.RoleRepository; 18 19 } 20 21 } 22 23 }
4.业务逻辑接口层
(1)那麽我们业务逻辑层需不要一个接口层呢?我觉得需要,为什么呢?因为我们涉及到层与层之间的调用了,UI层调用业务逻辑层,我们一定要依赖接口来编程。那麽下面我们来实现业务逻辑接口层,这个和数据访问接口层的作用基本一致。
(2)首先我们添加一个类库LYZJ.UserLimitMVC.IBLL,用来实现对于业务逻辑接口层的封装,然后我们新建三个接口:IUserInfoService,IRoleService,IBaseService。我们需要给这个类库添加Model层的引用。下面我们写出这三个接口层的代码,我这里就不解释了,和前面的意思都差不多。
(3) IBaseService接口
1 namespace LYZJ.UserLimitMVC.IBLL 2 3 { 4 5 public interface IBaseService<T> where T:class ,new () 6 7 { 8 9 // 实现对数据库的添加功能,添加实现EF框架的引用 10 11 T AddEntity(T entity); 12 13 //实现对数据库的修改功能 14 15 bool UpdateEntity(T entity); 16 17 //实现对数据库的删除功能 18 19 bool DeleteEntity(T entity); 20 21 //实现对数据库的查询 --简单查询 22 23 IQueryable<T> LoadEntities(Func<T, bool> whereLambda); 24 25 /// <summary> 26 27 /// 实现对数据的分页查询 28 29 /// </summary> 30 31 /// <typeparam name="S">按照某个类进行排序</typeparam> 32 33 /// <param name="pageIndex">当前第几页</param> 34 35 /// <param name="pageSize">一页显示多少条数据</param> 36 37 /// <param name="total">总条数</param> 38 39 /// <param name="whereLambda">取得排序的条件</param> 40 41 /// <param name="isAsc">如何排序,根据倒叙还是升序</param> 42 43 /// <param name="orderByLambda">根据那个字段进行排序</param> 44 45 /// <returns></returns> 46 47 IQueryable<T> LoadPageEntities<S>(int pageIndex, int pageSize, out int total, Func<T, bool> whereLambda, 48 49 bool isAsc, Func<T, S> orderByLambda); 50 51 } 52 53 }
(4) IUserInfoService接口
1 namespace LYZJ.UserLimitMVC.IBLL 2 3 { 4 5 public interface IUserInfoService:IBaseService<UserInfo> 6 7 { 8 9 } 10 11 }
(5) IRoleService接口
1 namespace LYZJ.UserLimitMVC.IBLL 2 3 { 4 5 public interface IRoleService:IBaseService<Role> 6 7 { 8 9 } 10 11 }
5.对业务逻辑层的再次修改
(1)当我们实现完业务逻辑接口层的时候,我们就需要让BLL去实现这个接口,我们给BLL的类库添加IBLL的引用,然后让UserInfoService,RoleService实现各自的继承关系,我这里就只贴出RoleService的代码,其他的都一样。
1 namespace LYZJ.UserLimitMVC.BLL 2 3 { 4 5 public class RoleService:BaseService<Role>,IRoleService 6 7 { 8 9 //重写抽象方法,设置当前仓储为Role仓储 10 11 public override void SetCurrentRepository() 12 13 { 14 15 //设置当前仓储为Role仓储 16 17 CurrentRepository = DAL.RepositoryFactory.RoleRepository; 18 19 } 20 21 } 22 23 }
6.小结
(1)到这里我们今天的业务逻辑层完成了,从今天开始,我们这个Demo的后台涉及已经全部完成,我们明天开始涉及其前台页面。今天的这篇文章本人感觉写的也不是很好,重点是语言太难组织,灵活性太大,所以需要看此博客的博友们我建议从第一篇博客看起,那麽就能很容易的理解这些讲的是什么。
(2)整体框架层图纸
(3)最终的Demo项目架构图
项目下载:明天晚上我会将项目上传,希望大家关注,谢谢!
Kencery返回本系列开篇