企业级应用架构(三)三层架构之数据访问层的改进以及测试DOM的发布
在上一篇我们在宏观概要上对DAL层进行了封装与抽象。我们的目的主要有两个:第一,解除BLL层对DAL层的依赖,这一点我们通过定义接口做到了;第二,使我们的DAL层能够支持一切数据访问技术,如Ado.net,EF,linq To Sql,这一点我们实现的不是很完美,仍有很大的改进空间,本文将加以改进。
在此之前我们来看一下我们最新的dom(PS:经过两天的赶工,我们的dom已经相对成熟,其中BLL层已经被我高度抽象化了,并且引进了业务上文文的概念;DAL层除了具体的技术实现尚为完成,其他方面已经相对完善了)
DAL层的AdoDal项目和EFDAL项目,分别代表采用ado.net技术和EF技术实现数据访问层,我在这两个项目中分别定义了一个OrderDAL测试类
和一个RolesDal测试类,代码如下
- using IDAL;
- using Model;
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace AdoDal
- {
- public class OderDAL : DALBase<Order>, IOrderDAL
- {
- public OderDAL(IDbConnection dbConnection)
- {
- }
- public override string TestMethod()
- {
- return "现在测试的是Ado的Oder类";
- }
- }
- }
- using IDAL;
- using Model;
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace AdoDal
- {
- public class RolesDal : DALBase<Roles>, IRoles
- {
- public RolesDal(IDbConnection dbConnection)
- {
- }
- public override string TestMethod()
- {
- return "现在测试的是Ado的roles类";
- }
- }
- }
- using EFDal;
- using IDAL;
- using Model;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace EFDal
- {
- public class OderDAL : DALBase<Order>, IOrderDAL
- {
- public override string TestMethod()
- {
- return "现在测试的是EF的Oder类";
- }
- }
- }
- using IDAL;
- using Model;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace EFDal
- {
- public class RolesDal : DALBase<Roles>, IRoles
- {
- public override string TestMethod()
- {
- return "现在测试的是EF的roles类";
- }
- }
- }
我们在BLL项目中的OrderBLL类来调用这RolesDal与OrderDAL的测试方法TestMethod()。注:我们的BLL层并没有引用DAL层,我们得到的DAL层实例,是通过工厂运用反射来实现的,至于,反射得到的OrderDAL,RolesDal是来自AdoDal项目还是EFDAL项目,完全是由我们的配置文件决定的,调用代码如下
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Model;
- using IDAL;
- using Common;
- using CommonFactory;
- using IBLL;
- namespace BLL
- {
- public class OrderBLL : BLLBase<OrderBusiness>,IOrderBLL
- {
- public OrderBLL()
- {
- BusinessContext = InstancesFactory.CreateInstances<IBusinessContext>
- (FactoryConfig.BusinessContext.AssemblyPath, FactoryConfig.BusinessContext.ClassName);
- }
- #region IOrderDAL的专用方法
- public string Test()
- {
- string str1= BusinessContext.DALSession.RolesDal.TestMethod();
- string str2= BusinessContext.DALSession.OrderDal.TestMethod();
- string str = string.Format("角色测试:{0} + 订单测试:{1}",str1,str2);
- return str;
- }
- #endregion
- }
- }
我们的UI层项目StructUI也实现了与BLL层的解耦,它并没有引用BLL,它得到的BLL层实例同样是采用工厂根据配置文件通过反射来实现的。如下
- using Common;
- using CommonFactory;
- using IBLL;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- namespace StructUI
- {
- public partial class Test : System.Web.UI.Page
- {
- protected IBussinessSession session;
- IBussinessSessionFactory factory;
- protected void Page_Load(object sender, EventArgs e)
- {
- factory = InstancesFactory.CreateInstances<IBussinessSessionFactory>
- (FactoryConfig.BussinessSessionFactory.AssemblyPath, FactoryConfig.BussinessSessionFactory.ClassName);
- session = factory.GetSession();
- string str= session.OrderBussiness.Test();
- txtInfo.InnerText = str;
- }
- }
- }
从上面我们知道,UI层的页面是通过工厂创建OrderBussiness实体,然后调用Test()方法,在把结果展示在前台的文本域中。好了,现在我们来开始测试。首先,我们通过配置文件来设置对EFDAL项目中的OrderDAL和RolesDal实体进行测试,我们配置文件如下
结果如图: 接着我们改变我们的配置文件,代码如下
结果如下:
综上,我们的框架实现了对数据访问层各类技术的支持,同时我们的成功的解除了框架中层与层的依赖(UI依赖BLL,BLL依赖DAL)。
下面我们来看一看目前版本的数据访问层相对于上一篇的数据访问层的改进,对照我们上一篇的项目结构(下图),我们发现在的数据访问层的DAL项目被干掉了,AdoDal,EFDal与DALFactory这三个新项目被添加进来了。
先说一说,我干掉DAL,同时又添加AdoDal,EFDal的原因。在上一篇文章,我们把对不同数据访问技术的实现寄托在ADOBase<T>类型与EFBase<T>类型上面,这两个类型最终将赋值给数据访问层基类的dalActive属性,来帮助基类实现数据层接口,至于是哪一个,则由配置文件说了算,我们采用工厂读取配置文件来创建这两个类型的实例,但是这里会碰到一个技术难题:这两个类型都是范型,我们无法事先知道该类型的范型参数在实例化时会是一个什么样的类型,所以我们没办法通过反射来动态读取程序集和类名创建对应的范型实例,因此,在上一篇我采用了一个非常简陋的工厂方来创建,如下
- using IDAL;
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.Linq;
- using System.Text;
- namespace DAL
- {
- public class DalActiveProvider<T> where T:class,new()
- {
- private static string className = ConfigurationManager.AppSettings["dalActiveName"];
- public static IDALBase<T> GetDalActive()
- {
- if (string.Equals(className, "EFBase"))
- {
- return new EFBase<T>();
- }
- else
- {
- return new ADOBase<T>();
- }
- }
- }
- }
这样我们就通过条件判断语句写死了程序数据访问层所能使用的技术,在这个工厂里面除了ado.net和EF,它将不会去创建其他任何技术的访问实体。我们想要增加一种新的技术则必须重新修改工厂的代码,这样就违背了软件工程的一个原则:一个好的框架,应该是在需要什么功能的时候去扩展,而不应该是去修改以前的代码。
另一个促使我改变程序框架的原因是因为我们目前的这种业务背景和抽象工厂模式相当的吻合。我们数据访问层采用什么技术,业务逻辑层根本就不关心,我们完全可以定义两个工厂来创建两种不同技术的实例,然后根据配置文件来决定采用哪一个工厂。但是这里我们必须设想一种情况,那就是我们的数据访问层的实体相当的多,如果我们每一个实体都用工厂来创建的话,那么配置文件肯定会很大,配置文件的节点一多起来,第一个不便于维护,第二个,不便于理解。因此,我们必须找到替代的方法,我在数据访问层定义了2个仓库类型:ADOSession与EFSession。其中ADOSession用于获取AdoDal项目中的所有数据访问实体,EFSession用于获取EFDal项目中的所有实体,代码如下
- using IDAL;
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace AdoDal
- {
- public class ADOSession : ISession
- {
- private IDbConnection dbConnection;
- public ADOSession(IDbConnection con)
- {
- dbConnection = con;
- }
- public IOrderDAL OrderDal
- {
- get
- {
- return new OderDAL(dbConnection);
- }
- }
- public IUsers UserDal
- {
- get
- {
- return new UsersDal(dbConnection);
- }
- }
- public IRoles RolesDal
- {
- get
- {
- return new RolesDal(dbConnection);
- }
- }
- }
- }
- using EFDal;
- using IDAL;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace EFAdo
- {
- public class EFSession : ISession
- {
- public IOrderDAL OrderDal
- {
- get
- {
- return new OderDAL();
- }
- }
- public IUsers UserDal
- {
- get
- {
- return new UsersDal();
- }
- }
- public IRoles RolesDal
- {
- get
- {
- return new RolesDal();
- }
- }
- }
- }
ADOSession类型中在构造函数中需要传入了一个IDbConnection数据库连接实体,很显然这个IDbConnection会来自BLL层,这是因为我们的业务层需要有定制事务的能力,因此它必须能够得到IDbConnection来发起事务,当一个事务被发起时,所有在事务期间被创建的数据访问实体对数据库的操作必须是基于事务发起这的IDbConnection,这样的操作,才受事务的控制。因此这就要求我们的BLL层在创建DAL数据实体,有定义该实体的IDbConnection的能力,很显然,在构造函数中传入统一的IDbConnection是一个不错的选择。
好了ADOSession,EFSession我们都有了,现在我们假设我们如果能够在BLL层拿到这样的实体,那么我们是不是就能够获得AdoDal或EFDal的所有实体呢?答案是显然的,但是这样问题又来了,我们BLL并没有引用DAL,所以这两个仓库实体什么类型BLL肯定是不知道的,BLL只认接口,因此我们必须为仓库定义接口,如下
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace IDAL
- {
- public interface ISession
- {
- IOrderDAL OrderDal
- {
- get;
- }
- IUsers UserDal
- {
- get;
- }
- IRoles RolesDal
- {
- get;
- }
- }
- }
另外,我们还必须有相应的实例化机制,给BLL层的调用者提供实例化服务。因此我们想到提供两套数据访问层实例工厂来为调用者提供实例化,至于到底选择哪一套工厂,则完全由配置文件说了算。我们的两个工厂都实现了工厂接口,代码如下
- using AdoDal;
- using IDAL;
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using System.Text;
- namespace DALFactory
- {
- public class ADOSessionFactory:ISessionFactory
- {
- private IDbConnection dbConnection;
- public ADOSessionFactory(IDbConnection con)
- {
- dbConnection = con;
- }
- public ISession GetSession()
- {
- return new ADOSession(dbConnection);
- }
- }
- }
- using EFAdo;
- using IDAL;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace DALFactory
- {
- public class EFSessionFactory : ISessionFactory
- {
- public ISession GetSession()
- {
- return new EFSession();
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace IDAL
- {
- public interface ISessionFactory
- {
- ISession GetSession();
- }
- }
在BLL层的业务上下文中,我们把对应的ISessionFactory在构造函数中通过工厂读取配置文件进行实例化,代码如下
- using Common;
- using CommonFactory;
- using IBLL;
- using IDAL;
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Data.SqlClient;
- using System.Linq;
- using System.Text;
- namespace BLL
- {
- public class AdoBussinessContext : IBusinessContext
- {
- /// <summary>
- /// 链接字符串
- /// </summary>
- protected IDbConnection Con{ get; private set; }
- protected ISessionFactory sessionFactory { get; private set; }
- public AdoBussinessContext()
- {
- ConnectionFactory factory = new ConnectionFactory("str");
- Con = factory.CreateConnection();
- sessionFactory = InstancesFactory.CreateInstances<ISessionFactory>(FactoryConfig.SessionFactory.AssemblyPath,
- FactoryConfig.SessionFactory.ClassName, new object[]{ Con });
- }
- public ISession DALSession
- {
- get
- {
- return sessionFactory.GetSession();
- }
- }
- public IDbTransaction BeginTransaction(IsolationLevel level)
- {
- return Con.BeginTransaction(level);
- }
- public IDbTransaction BeginTransaction()
- {
- return Con.BeginTransaction();
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Reflection;
- namespace CommonFactory
- {
- public class InstancesFactory
- {
- /// <summary>
- /// 创建指定类型T的实例
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="assemblyPath">程序集路径</param>
- /// <param name="className">类名称(非全名)</param>
- /// <returns>T类型实例</returns>
- public static T CreateInstances<T>(string assemblyPath, string className, object[] args = null)
- {
- className = string.Format("{0}.{1}", assemblyPath, className);
- Assembly assembly = Assembly.Load(assemblyPath);
- T instances;
- if(args!=null)
- {
- instances=(T)assembly.CreateInstance(className, true, BindingFlags.Default, null, args, null, null);
- }
- else
- {
- try
- {
- instances = (T)assembly.CreateInstance(className);
- }
- catch(Exception ex)
- {
- throw ex;
- }
- }
- return instances;
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.Linq;
- using System.Text;
- namespace Common
- {
- public class ConfigEntity
- {
- /// <summary>
- /// 程序路径
- /// </summary>
- public string AssemblyPath { get; set; }
- /// <summary>
- /// 类命
- /// </summary>
- public string ClassName { get; set; }
- }
- public class FactoryConfig
- {
- public static ConfigEntity SessionFactory
- {
- get
- {
- return new ConfigEntity()
- {
- AssemblyPath = ConfigurationManager.AppSettings["SessionFactoryAssemblyPath"],
- ClassName = ConfigurationManager.AppSettings["SessionFactory"]
- };
- }
- }
- public static ConfigEntity BusinessContext
- {
- get
- {
- return new ConfigEntity()
- {
- AssemblyPath = ConfigurationManager.AppSettings["BusinessContextAssemblyPath"],
- ClassName = ConfigurationManager.AppSettings["BusinessContext"]
- };
- }
- }
- public static ConfigEntity BussinessSessionFactory
- {
- get
- {
- return new ConfigEntity()
- {
- AssemblyPath = ConfigurationManager.AppSettings["BussinessSessionFactoryAssemblyPath"],
- ClassName = ConfigurationManager.AppSettings["BussinessSessionFactory"]
- };
- }
- }
- }
- }
配置文件参见本文DOM演示部分,在BLL层的业务上下文我们就可以通过ISessionFactory拿到ISession实体了,有了ISession实体我们就有了基于一种数据访问技术的所有数据访问层的实体了。我们BLL层可以大摇大摆的调用我们的数据访问实体操作数据库了。至此我们的数据访问层的抽象已经基本完成,剩下来的就是把数据访问层AdoDal,EFDal两个项目中的具体技术细节全部实现,这将是我后续文章的内容呢.......
总结
本文在上一篇文章的基础上面继续优化了数据访问层,使我们的数据访问层更加的完善与成熟。一个好的应用框架总是运用中被不断的完善,我们在开发的时候,多想一想我们所用框架的局限性,我们总能找到相应的优化点,最后感谢大家的观看,本文DOM的源码请点击 这里
企业级应用架构(三)三层架构之数据访问层的改进以及测试DOM的发布的更多相关文章
- 数据访问层的改进以及测试DOM的发布
数据访问层的改进以及测试DOM的发布 在上一篇我们在宏观概要上对DAL层进行了封装与抽象.我们的目的主要有两个:第一,解除BLL层对DAL层的依赖,这一点我们通过定义接口做到了:第二,使我们的DAL层 ...
- 架构(三层架构)、框架(MVC)、设计模式三者异同点
前言: 本博客主要针对架构.框架和设计模式三者的区别.还有三层和MVC的区别进行讨论.对于这三者一点都不了解的.请点在维基和百度百科上补补课.这里就不发链接了 软件架构(software archit ...
- 【2017-04-20】Ado.Net与面向对象结合架构中的数据访问层(实体类,数据访问类)
开发项目三层架构:界面层.业务逻辑层.数据访问层 今天学习一下数据访问层,分为实体类和数据访问类 所有的类放在App_Code这个文件夹下边.养成一个好的习惯. 一.实体类 数据库中的表映射为一个类, ...
- 项目架构开发:数据访问层之Cache
数据访问层简单介绍 数据访问层,提供整个项目的数据访问与持久化功能.在分层系统中所有有关数据访问.检索.持久化的任务,最终都将在这一层完成. 来看一个比较经典的数据访问层结构图 大概可以看出如下信息 ...
- 项目架构开发:数据访问层之Logger
接上文 项目架构开发:数据访问层之Cache 本章我们继续ILogger的开发 ILogger.cs public interface ILogger { void Info(object messa ...
- 项目架构开发:数据访问层之Repository
接上文 项目架构开发:数据访问层之Logger 本章我们继续IRepository开发,这个仓储与领域模式里边的仓储有区别,更像一个工具类,也就是有些园友说的“伪仓储”, 这个仓储只实现单表的CURD ...
- 项目架构开发:数据访问层之UnitOfWork
接上文 项目架构开发:数据访问层之IQuery 本章我们继续IUnitOfWork的开发,从之前的IRepository接口中就可以看出,我们并没有处理单元事务, 数据CUD每次都是立即执行的,这样有 ...
- 项目架构开发:数据访问层之Query
接上文 项目架构开发:数据访问层之Repository 上一章我们讲了IRepository接口,这张我们来讲IQuery 根据字面意思就可以知道,这次主要讲数据查询,上一章我们只针对单表做了查询的操 ...
- 项目架构开发:数据访问层之UnitOfWork (补充)
应lisansi同学回复(项目架构开发:数据访问层之UnitOfWork)要求,补上Dapper的DbContext实现 using Dapper.Contrib.Extensions; using ...
随机推荐
- sql 日期时间格式转换
Sql日期时间格式转换 sql server2000中使用convert来取得datetime数据类型样式(全) 日期数据格式的处理,两个示例: CONVERT(varchar(16), 时间一, ...
- Java之 AtomicInteger
AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字.而AtomicIn ...
- C#调用C++的DLL函数另一则(delegate) z
使用DLLImport进行导入函数的事. C#调用C++的函数其实不止这一种方法, 还有一种方法是用delegate申明函数委托进行调用,这种方法略显麻烦,但是可以进行回调并应用指针. 在C#中,首先 ...
- STL总结之list
STL中list和我们传统意义上的链表一样可以进行动态节点添加和释放. 优点:适合动态删除和添加 缺点:不支持随机访问. C++标准对list模板声明: template < class T ...
- lightoj 1007
预先处理好phi数组和前缀和,水题. #include<cstdio> #include<string> #include<cstring> #include< ...
- jquery 列求和
列求和 var m = 0; $('#tb tr').each(function () { //td:eq(3)从0开始计数 $(this).find('td:eq(3)').each(functio ...
- ActiveMQ的安全机制使用及其源代码分析 [转]
ActiveMQ是目前较为流行的一款开源消息服务器.最近在项目开发中,需要为ActiveMQ开发基于IP的验证和授权机制,因此,对ActiveMQ的安全机制进行了了解,以下将介绍ActiveMQ的安全 ...
- (2)I2c总线SDA\SCL以及开始终止条件
I2C只用两条线(SDA和SCL)在连接到总线上的设备之间传送数据.每一个设备都由唯一的地址来识别(不管是微处理器.LCD驱动器.存储器或者键盘接口),并且可以依照设备的功能作为发送器或者接收器使用. ...
- HDU 1114 iggy-Bank(完全背包)
水 给出小猪钱罐的重量和装满钱后的重量,然后是几组数据,每组数据包括每种钱币的价值与重量 要求出重量最少能装满钱罐时的最大价值 #include<iostream> #include< ...
- Win8关机 一直重启的问题 解决方案-摘自网络
win8关机时自动重启的解决方法:关闭快递启动,默认是启动的,到控制面板 – 电源选项 – 选择电源按钮的功能 – 更改当前不可用的设置 – 关机设置 – 把勾去掉 关闭快速启动.这样就不会关机一直重 ...