在上一篇我们在宏观概要上对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类";}}
}

View Code

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类";}}
}

View Code

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类";}}
}

View Code

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类";}}
}

View Code

我们在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}
}

View Code

我们的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;} }
}

View Code

从上面我们知道,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>();}}}
}

View Code

这样我们就通过条件判断语句写死了程序数据访问层所能使用的技术,在这个工厂里面除了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); } } } }

View Code

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(); } } } }

View Code

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;}}
}

View Code

另外,我们还必须有相应的实例化机制,给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);}}
}

View Code

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();}}
}

View Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace IDAL
{public interface ISessionFactory{ISession GetSession();}
}

View Code

在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();}}
}

View Code

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;}}
}

View Code

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"]};}}}
}

View Code

配置文件参见本文DOM演示部分,在BLL层的业务上下文我们就可以通过ISessionFactory拿到ISession实体了,有了ISession实体我们就有了基于一种数据访问技术的所有数据访问层的实体了。我们BLL层可以大摇大摆的调用我们的数据访问实体操作数据库了。至此我们的数据访问层的抽象已经基本完成,剩下来的就是把数据访问层AdoDal,EFDal两个项目中的具体技术细节全部实现,这将是我后续文章的内容呢.......

总结

本文在上一篇文章的基础上面继续优化了数据访问层,使我们的数据访问层更加的完善与成熟。一个好的应用框架总是运用中被不断的完善,我们在开发的时候,多想一想我们所用框架的局限性,我们总能找到相应的优化点,最后感谢大家的观看,本文DOM的源码请点击  这里

转载于:https://www.cnblogs.com/shaoshun/p/3813640.html

企业级应用架构(三)三层架构之数据访问层的改进以及测试DOM的发布相关推荐

  1. ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发

    为什么80%的码农都做不了架构师?>>>    ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发          前言:本篇主要讲述数据访问层的开发, ...

  2. Scott Mitchell 的ASP.NET 2.0数据教程之一: 创建一个数据访问层

    原文 | 下载本教程中的编码例子 | 下载本教程的英文PDF版 导言 作为web开发人员,我们的生活围绕着数据操作.我们建立数据库来存储数据,写编码来访问和修改数据,设计网页来采集和汇总数据.本文是研 ...

  3. 企业级应用架构(二)三层架构之数据访问层的封装与抽象

    在上一篇我们知道,要解除BLL对DAL的依赖,我们就必须抽象出DAL层的接口,同时基于DAL的数据访问技术很多,如EF,ADO.NET,LINQ TO SQL,因此,我们的数据访问层必须对这些技术提供 ...

  4. 三层架构:表示层——业务逻辑层——数据访问层

    三层架构:表示层--业务逻辑层--数据访问层 1.什么是三层架构 所谓的三层开发就是将系统的整个业务应用划分为表示层--业务逻辑层--数据访问层,这样有利于系统的开发.维护.部署和扩展. 分层是为了实 ...

  5. 三层架构:表示层-业务逻辑层-数据访问层

    三层架构和MVC是两个东西. 非要相关的话: 三层架构中"表现层"的aspx页面对应MVC中的View(继承的类不一样) 三层架构中"表现层"的aspx.cs页 ...

  6. java三层架构(表示层,业务逻辑层,数据访问层,与两层的区别,三层架构的优缺点)

    目录 1 什么是三层 2 为什么使用三层 3 与两层的区别 1 什么是三层 UI(表现层): 主要是指与用户交互的界面.用于接收用户输入的数据和显示处理后用户需要的数据. BLL:(业务逻辑层): U ...

  7. 三层架构:表示层-业务逻辑层-数据访问层2

    概述 在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构.微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层.业务逻辑层(又或称为领域层).表示层. 三层结构原理: 3个层次中, ...

  8. 企业级应用架构(一) 三层架构之解耦

    前言 前段时间朋友拿了个网站给我,让我帮忙添加几个小功能,我爽快的答应了,但是当我打开源码,我瞬间就奔溃了,整个项目连最基本的三层框架也没有搭建,仅仅是封装了一个sqlhelp作为数据库的操作接口,项 ...

  9. java数据访问层实例_java实际项目先写哪一层?java三层架构是什么?

    在写java实际项目的时候,我们都是要有思维逻辑的,哪一层先写哪一层后写,这些都要考虑清楚,那么接下来,我们就来给大家讲解一下这方面的内容. 这没有标准,一般是controller-service-d ...

最新文章

  1. [LeetCode]: 242: Valid Anagram
  2. UITableView 滑动删除
  3. OC中protocol、category和继承的区别
  4. python排序函数set_【Python】排列组合itertools 集合set
  5. 微服务中集成分布式配置中心 Apollo
  6. B. Who‘s Opposite?
  7. 中英文搜索引擎收录口整理
  8. Hadoop 2.4.1 设置问题小结【原创】
  9. 键盘响应c语言,c 键盘响应
  10. 关于IE插件开发(一)——COM组件开发加法
  11. Ant Design Vue实现表格序号自动递增
  12. Spring Cloud技术栈简述
  13. 百度 UNIT 使用
  14. 英读廊——为什么打印机不使用RGB颜色体系而是CMYK体系
  15. 笔记本电脑应用商店服务器错误,打开win10商店出错 出现win10商店请稍后重试问题怎么办 - 驱动管家...
  16. win7服务器如何还原系统教程,怎么快速处理win7系统架设本地服务器的还原技巧...
  17. 转载——背包九讲(原文链接已不可考)
  18. 数学建模|预测方法:马尔科夫预测
  19. Docker 1 - 概述
  20. VT-x/AMD-V 硬件加速在您的系统中不可用。您的 64-位虚拟机将无法检测到 64-位处理器,从而无法启动。

热门文章

  1. python中sorted的用法append_python sorted()排序详解
  2. leetcode 42. 接雨水 思考分析(暴力、动态规划、双指针、单调栈)
  3. kotlin 查找id_Kotlin程序查找给定范围内的素数
  4. Java ObjectOutputStream writeFields()方法与示例
  5. 螺旋遍历_螺旋形式的水平阶遍历
  6. 20160828_第4周周报
  7. 121. 买卖股票的最佳时机 golang
  8. Makefile使用及多文件gdb 调试
  9. linux 安装qt 4.6软件,QT学习之一:Linux下安装QT之版本qt-4.6.3
  10. Linux惊群效应详解(最详细的了吧)