企业级应用架构(三)三层架构之数据访问层的改进以及测试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类";}}
}
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/399288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctr多少正常_亚马逊广告ctr多少才算合格,如何提升亚马逊CTR

亚马逊广告ctr多少才算合格&#xff0c;如何提升亚马逊CTR很多亚马逊卖家并不重视点击率&#xff0c;其实点击率和转化率是同样重要的。好的点击率可以提升产品listing的流量&#xff0c;间接影响转化&#xff0c;促进销量。一般来说亚马逊点击率多少算正常呢&#xff1f;CTR全…

在linux中,我为什么不能安装VMware Tools?

在linux中&#xff0c;我为什么不能安装VMware Tools&#xff1f; 应该是操作不正确导致&#xff0c;以下为linux安装VMware Tools的方法。 1、在安装Linux的虚拟机中&#xff0c;单击“虚拟机”菜单下的“安装Vmware-Tools” 2、出现以下菜单&#xff0c;证明Vmware-Tools的光…

判断 CGRect是否“为空”

2019独角兽企业重金招聘Python工程师标准>>> property (nonatomic, assign) CGRect prototypeRect; -----这样的声明应该没有问题的&#xff0c;的if(!self.prototypeRect)报错是因为 CGRect是结构体&#xff0c;不能作非nil判断&#xff0c;你可以利用self.protot…

Javascript学习总结 - JS基础系列 二

简述 本系列将持续更新Javascript基础部分的知识&#xff0c;谁都想掌握高端大气的技术&#xff0c;但是我觉得没有一个扎实的基础&#xff0c;我认为一切高阶技术对我来讲都是过眼云烟&#xff0c;要成为一名及格的前端工程师&#xff0c;必须把基础打扎实了。我也想展翅高飞&…

mysql 可逆编码_使用MD5编码实现数据库用户密码字段的加密

1 前言 众所周知&#xff0c;MD5是目前应用最多的密码保护方法&#xff0c;该编码传说为不可逆加密编码&#xff1c;也就是说&#xff0c;永运无法倒算原码&#xff1e;。 使用MD5加密用户的操作密码&#xff0c;可以有效防止系统维护人员直接进入数据库时出现系统安全漏洞&…

Unity3D 与 objective-c 之间数据交互。iOS SDK接口封装Unity3D接口

原地址&#xff1a;http://www.cnblogs.com/qingjoin/p/3638915.html Unity 3D 简单工程的创建。与Xcode 导出到iOS 平台请看这 Unity3D 学习 创建简单的按钮、相应事件 Unity C# 代码 using UnityEngine; using System.Collections; using System.Runtime.InteropServices;pu…

开源代码ViewPageIndicator的使用

1. 导入Android studio 使用SlidingMenu的方式导入Android studio不行&#xff0c;不知道为何&#xff0c;过会懂了再写上 2. 代码 activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.…

[unity3d]手游资源热更新策略探讨

原地址&#xff1a;http://blog.csdn.net/dingxiaowei2013/article/details/20079683 我们学习了如何将资源进行打包。这次就可以用上场了&#xff0c;我们来探讨一下手游资源的增量更新策略。注意哦&#xff0c;只是资源哦。关于代码的更新&#xff0c;我们稍后再来研究。理论…

PostgreSQL学习手册(二) 模式(Schema)

2019独角兽企业重金招聘Python工程师标准>>> 一个数据库包含一个或多个命名的模式&#xff0c;模式又包含表。模式还包含其它命名的对象&#xff0c;包括数据类型、函数&#xff0c;以及操作符。同一个对象名可以在不同的模式里使用而不会导致冲突&#xff1b; 比如…

软件工作第4次作业

软件工作第4次作业 信管141 宋乃佳 1425052010 基于我们列出的 7 条UX评价准则&#xff0c;分析“师路南通” 在用户体验设计方面让你觉得满意的地方&#xff08;不少于2点&#xff09;&#xff1b;&#xff08;20分&#xff09;&#xff0c;请陈述理由。 同样&#xff0c;分析…

phoneGap2.9+eclipse开发环境和helloword案例

不同机器安装和使用各不相同&#xff0c;这里也只是记录一下自己机器上面的使用过程。 android安装环境前面的文章有些&#xff0c;这里不再说&#xff0c;直接上phoneGap的过程。因为phoneGap2.9.1需要安装nodejs和Git&#xff0c;比较复杂&#xff0c;没有太多精力去折腾&…

Ubuntu14.04下搭建Bochs仿真平台,同时用该平台安装Linux0.11内核

因为Linux0.11内核需要在80X86硬件平台上运行&#xff0c;现在已经没有该硬件系统了&#xff0c;所以需要搭建Bochs这个仿真平台。Bochs是一个X86硬件平台的开源模拟器。 安装步骤参考的是如下一篇文章&#xff1a;http://os.51cto.com/art/201407/446838_all.htm&#xff0c;非…

java web与android互通的aes算法

2019独角兽企业重金招聘Python工程师标准>>> ####Java实现代码 //可自定义保证16btye即可private static final byte[] IV {16, 26, -35, 23, 34, 125, -5, -4, -8, -9, -15, -78, 90, -8, -99, 100};public static byte[] encrypt(String content, String passwor…

mysql第三方工具binlog_mysql 开发进阶篇系列 33 工具篇(mysqlbinlog日志管理工具)

一.概述由于服务器生成的二进制日志文件以二进制格式保存&#xff0c;所以如果要想检查这些文件的文本格式&#xff0c;就会用到mysqlbinlog日志管理工具。mysqlbinlog的语法如下:mysqlbinlog [options] log-files log-files2...其中options有很多选项&#xff0c;常用如下&…

JMeter部分功能详解

JMeter 介绍&#xff1a; 一个非常优秀的开源免费的性能测试工具。 优点&#xff1a;你用着用着就会发现它的重多优点&#xff0c;当然不足点也会呈现出来。 从性能工具的原理划分&#xff1a; Jmeter工具和其他性能工具在原理上完全一致&#xff0c;工具包含4个部分&#xff1…

登录时记住用户名和密码及cookie案例应用

文章原址&#xff1a;http://www.jb51.net/article/33588.htm 登录样子&#xff0c;可以参考某一论坛的登录介面&#xff1a; 记住这些信息&#xff0c;可以使用Cookie来实现&#xff0c;更多Cookie应用&#xff0c;可参考 http://jb51.net/article/33590.htm http://jb51.net…

退出登录后点返回键 是登录状态_看了这50条登录的测试点,你还敢说测试很容易吗...

条件&#xff1a;一个用户名输入框 (要求15个字符以内)一个密码输入框 (要求8个字符以内)一个登录按钮针对以上条件进行测试用例的设计先回顾一下测试用例的设计方法&#xff1a;等价类&#xff0c;边界值&#xff0c;错误猜测法&#xff0c;因果图&#xff0c;场景法测试功能点…

HTML5原生拖拽/拖放(drag drop)详解

前言 拖放&#xff08;drap && drop&#xff09;在我们平时的工作中&#xff0c;经常遇到。它表示&#xff1a;抓取对象以后拖放到另一个位置。目前&#xff0c;它是HTML5标准的一部分。我从几个方面学习并实践这个功能。 拖放的流程对应的事件 我们先看下拖放的流程&a…

Linux vmstat命令详解

vmstat命令是最常见的Linux/Unix监控工具&#xff0c;可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率&#xff0c;内存使用&#xff0c;虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令&#xff0c;一个是Linux/Unix都支持&#xff0c;二是…

python中如何比较两个列表_python中如何比较两个列表

cmp() 方法用于比较两个列表的元素。cmp()方法语法&#xff1a;cmp(list1, list2)参数&#xff1a;list1 -- 比较的列表。list2 -- 比较的列表。返回值&#xff1a;如果比较的元素是同类型的,则比较其值,返回结果。如果两个元素不是同一种类型,则检查它们是否是数字。如果是数字…