OA之框架的搭建
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 - ) * pageSize).Take<T>(pageSize);
}
else
{
totalEntities = totalEntities.OrderByDescending<T, S>(orderLambda).Skip<T>((pageIndex - ) * 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() > ;
} public bool DeleteEntity(T entity )
{
dbContext.Entry<T>(entity ).State = EntityState.Deleted;
return dbContext.SaveChanges() > ;
}
}
}
上面的代码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()>;
}
}
}
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 - ) * pageSize).Take<T>(pageSize);
}
else
{
totalEntities = totalEntities.OrderByDescending<T, S>(orderLambda).Skip<T>((pageIndex - ) * 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一个对象,实际上就是一个工厂类,与其在每一各类中都写一遍保证线程内唯一的判断,不如直接使用工厂
namespace OA.DAL
{
public static class DBContextFactory
{
//工厂类的作用就是创建实例对象,可以包括相同的实例,也可以是不相同的实例,同时使用CallContext(这个对象跟HttpContext作用是一样的)实现线程内唯一对象 public Model.OAEntities CreateDBContext()
{
Model.OAEntities dbContext = (Model.OAEntities)CallContext.GetData("dbContext");
if (dbContext == null)
{
dbContext = new Model.OAEntities();
CallContext.SetData("dbContext", dbContext);
}
return dbContext;
}
}
}
这个工厂比较特殊,因为他是生产的是同一个对象,它可以随便找个类库存放,但是不能存放在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为例
namespace OA.BLL
{
public class UserService:BaseService<Users>,IUserService
{
//并不会手动去调用这个方法,所以没必要写在接口里
public override void SetCurrentDal()
{
//在具体的业务子类中可以确定确定具体的是哪一个Dal
CurrentDal = CurrentDBSession.UserDal;
}
}
}
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层
搭建完毕。
OA之框架的搭建的更多相关文章
- 基于C/S架构的3D对战网络游戏C++框架_06搭建C/S架构的基本通信框架(尚未写完会重新编辑后再发出)
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- 基于C/S架构的3D对战网络游戏C++框架_05搭建系统开发环境与Boost智能指针、内存池初步了解
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- Spring+SpringMvc+Mybatis框架集成搭建教程
一.背景 最近有很多同学由于没有过SSM(Spring+SpringMvc+Mybatis , 以下简称SSM)框架的搭建的经历,所以在自己搭建SSM框架集成的时候,出现了这样或者那样的问题,很是苦恼 ...
- eclipse中SSH三大框架环境搭建<三>
相关链接: eclipse中SSH三大框架环境搭建<一> eclipse中SSH三大框架环境搭建<二> 引言:通过上两篇文章我们已经可以掌握struts2和spring的环境的 ...
- eclipse中SSH三大框架环境搭建<二>
通过上一篇博客我们可以轻松搭建strtus2的环境,接下来由我来继续介绍spring的环境搭建以及spring注入的简单使用 相关链接:eclipse中SSH三大k框架环境搭建<一> ec ...
- Struts2+Spring+Hibernate(SSH)框架的搭建
首先需要下载struts2 ,spring4,hibernate5 的资源包; struts2资源包下载路径:http://www.apache.org/spring资源包下载路径:http://p ...
- Ext.NET 4.1 系统框架的搭建(后台) 附源码
Ext.NET 4.1 系统框架的搭建(后台) 附源码 代码运行环境:.net 4.5 VS2013 (代码可直接编译运行) 预览图: 分析图: 上面系统的构建包括三块区域:North.West和C ...
- eclipse中SSH三大框架环境搭建<一>
这里先简单介绍一下我用的三大框架版本以及下载地址 相关链接:eclipse中SSH三大框架环境搭建<二> eclipse中SSH三大框架环境搭建<三> struts-2.3.3 ...
- iOS基础框架的搭建/国际化操作
1.基础框架的搭建 1.1 pod引入常用的第三方类库 1.2 创建基础文件夹结构/目录结构 Resource———存放声音/图片/xib/storyboard 等资源文件 Define——宏定义, ...
- iOS之UI--主流框架的搭建--仿制QQ的UI框架
使用XCode搭建多个控制器界面,一般在实际开发中建议超过四个控制器界面使用纯代码. 下面的实例其实已经超过了四个,总结详细步骤的目的,主要是更熟悉XCode的StoryBoard使用细节. 先直接上 ...
随机推荐
- Mac之安装zsh
1.安装homebrew ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/in ...
- Surfer 高并发双核无头浏览器 (Golang语言)
Surfer A high level concurrency downloader. surfer是一款Go语言编写的高并发爬虫下载器,拥有surf与phantom两种下载内核. 支持固定Use ...
- Nginx的启动与停止,重启
1.先确定nginx所在的文件位置 如: 重启 1.验证nginx配置文件是否正确 方法一:进入nginx安装目录sbin下,输入命令./nginx -t 2.重启Nginx服务 方法一:进入ngin ...
- memcached +mysql+php 例子
<?php header("content-type:text/html;charset=utf-8"); $memcachehost = '127.0.0.1'; $mem ...
- 访问GitLab的PostgreSQL数据库
1.登陆gitlab的安装服务查看配置文件 cat /var/opt/gitlab/gitlab-rails/etc/database.yml production: adapter: postgre ...
- JAVA语言基础内部测试题(50道选择题)
JAVA语言基础内部测试题 选择题(针对以下题目,请选择最符合题目要求的答案,针对每一道题目,所有答案都选对,则该题得分,所选答案错误或不能选出所有答案,则该题不得分.)(每题2分) 没有注明选择几项 ...
- Nginx 链接
Nginx反向代理以及负载均衡配置:http://www.cnblogs.com/Miss-mickey/p/6734831.html
- python2.0_day21_web聊天室一
bbs系统项目中我们用到的ajax不多,但是在聊天室里用到的全是ajax,所以本项目的主要内容就是:前端使用ajax传输json格式的数据是本节的重点以及 前端函数的的使用.http协议的特点是:短链 ...
- ScrollView拉到尽头时出现阴影的解决方法
<code class="hljs markdown has-numbering" style="display: block; padding: 0px; col ...
- brocadcastReceiver
用来接收广播, 可以根据系统发生的一些时间做出一些处理 系统的一些事件,比如来电,来短信,等等,会发广播:可监听这些广播,并进行一些处理: Android3.2以后,为了安全起见,对于刚安装的应用,需 ...