在本系列的第一篇随笔《Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)》中介绍了Entity Framework 实体框架的一些基础知识,以及构建了一个简单的基于泛型的仓储模式的框架,例子也呈现了一个实体框架应用的雏形,本篇继续介绍这个主题,继续深化介绍Entity Framework 实体框架的知识,以及持续优化这个仓储模式的实体框架,主要介绍业务逻辑层的构建,以及利用Unity和反射进行动态的对象注册。

1、EDMX文件位置的调整

我们从上篇例子,可以看到这个随笔介绍的仓储模式的实体框架结构如下所示。

但实际上上篇随笔的例子是有点理想化的了,因为我们知道,【ADO.NET实体数据模型】生成的EDMX文件实质上自动生成了数据访问的上下文SqlserverContext,以及几个表的实体类,具体的效果如下所示。

我们理想化的把它放到DAL目录,Entity目录下,实际上是不可以的,至少是有冲突的。

那么我们应该如何处理,才能比较合理的处理这些自动生成的内容呢?另外我们已经把它上升了一层到业务层,具体的BLL分层如何处理数据访问对象的呢,通过什么方式构建数据访问对象?带着这些问题,我们再来一步步分析这个框架的内容。

为了给实体类友好的名称,我们顺便把表名的前缀移除了,如EDMX的图形如下所示。

为了比较好的利用EDMX文件的代码生成,我们把这个文件整体性的移动到了Entity目录下,如下所示。

这样相当于把数据访问的上下文,以及实体类的代码全部移动到Entity命名空间里面去了,虽然可能感觉不太好,但是我们先让它跑起来,具体的细节后面在优化完善。

2、业务逻辑层的设计

我们再来关注下业务逻辑层(包括业务逻辑接口层),和数据访问层类似,我们把它构建如下所示。

1)业务逻辑接口层

    /// <summary>/// 业务逻辑层基类接口/// </summary>/// <typeparam name="T">实体对象类型</typeparam>public interface IBaseBLL<T> where T : class{                T Get(object id);IList<T> GetAll(Expression<Func<T, bool>> whereCondition);IList<T> GetAll();}

2)业务逻辑层实现

    /// <summary>/// 业务逻辑基类/// </summary>/// <typeparam name="T">实体对象类型</typeparam>public abstract class BaseBLL<T>: IBaseBLL<T>  where T : class{protected IBaseDAL<T> baseDAL { get; set; }public BaseBLL(IBaseDAL<T> dal){this.baseDAL = dal;}public T Get(object id){return baseDAL.Get(id);}public IList<T> GetAll(Expression<Func<T, bool>> whereCondition){return baseDAL.GetAll(whereCondition);}public IList<T> GetAll(){return baseDAL.GetAll();}}

3)业务对象类的逻辑接口层

    /// <summary>/// 城市的业务对象接口/// </summary>public interface ICityBLL : IBaseBLL<City>{}

4)业务对象的逻辑层实现

    /// <summary>/// 城市的业务对象/// </summary>public class CityBLL : BaseBLL<City>{protected ICityDAL dal;public CityBLL(ICityDAL dal) : base(dal){this.dal = dal;}}

上面基本上完整的阐述了业务逻辑层的实现了,不过我们看到一个问题,就是不管是逻辑层基类,还是具体业务对象的逻辑对象,都没有默认构造函数,我们不能使用new进行对象的创建!

这是一个严重的问题,那么我们如何才能规避这个问题,能够使我们的业务对象类能够使用默认函数,使用new创建对象呢?这里我们需要引入IOC容器做法,也就是使用微软的Unity进行对象的注入及使用。

3、使用Unity实现对象的依赖注入

1)Unity的简单介绍

Unity是Unity是微软patterns& practices组用C#实现的轻量级,可扩展的依赖注入容器,它为方便开发者建立松散耦合的应用程序,

有以下优点:

1.简化了对象的创建,特别是针对分层对象结构和依赖关系;

   2.需求的抽象,允许开发人员在运行时或配置文件中指定依赖关系,简化横切关注点的管理;

   3.推迟为容器配置组件的时机,增加了灵活性;

   4.服务定位能力,这使客户能够存储或缓存容器;

5.实例和类型拦截

Unity的依赖注入使用例子比较容易理解,具体代码如下所示。

 static void Main( string[] args ){//实例化一个控制器IUnityContainer unityContainer = new UnityContainer();//实现对象注入unityContainer.RegisterType<IBird, Swallow>();IBird bird = unityContainer.Resolve<IBird>();bird.Say();
}

这个Unity的对象,我们可以通过Nuget进行添加即可,添加后,在项目里面就有对应对应的程序集引用了。

2)引入Unity实现数据访问对象注入,完善逻辑层实现

了解了Unity的使用,我们可以在BaseBLL对象基类类里面构建一个IOC的容器,并在这个容器初始化的时候,注册对应的数据访问层对象即可,如下所示。

    /// <summary>/// 业务逻辑基类/// </summary>/// <typeparam name="T">实体对象类型</typeparam>public abstract class BaseBLL<T>: IBaseBLL<T>  where T : class{private static readonly object syncRoot = new Object();protected IBaseDAL<T> baseDAL { get; set; }protected IUnityContainer container { get; set; }/// <summary>/// 默认构造函数。/// 默认获取缓存的容器,如果没有则创建容器,并注册所需的接口实现。/// </summary>public BaseBLL() {lock (syncRoot){container = DALFactory.Instance.Container;if (container == null){throw new ArgumentNullException("container", "container没有初始化");}}}

好,默认在DALFactory的类里面,我们就是在其实例化的时候,把需要的数据访问对象压进去,这样我们就可以在具体的业务对象逻辑类里面实现调用,如下代码所示。

    /// <summary>/// 城市的业务对象/// </summary>public class CityBLL : BaseBLL<City>{protected ICityDAL dal;public CityBLL()  // 当创建一个派生类的对象时,系统首先自动创建一个基类对象,也就是说,在调用派生类构造函数创建派生类对象之前,系统首先调用基类的构造函数创建基类对象。当派生类对象生命期结束时,首先调用派生类的析构函数,然后调用基类的析构函数。简而言之,就是说,构造函数:基类->派生类。析构函数:派生类->基类。{dal = container.Resolve<ICityDAL>();baseDAL = dal;}public CityBLL(ICityDAL dal) : base(dal){this.dal = dal;}}

如果我们不关心DALFactory里面的构架细节,这个框架已经完成的对象的注入,可以正常使用了。

但是我们还是来看看它的实现细节,我们通过单例模式(饿汉模式)构架IOC容器并注入相应的DAL对象了。

    /// <summary>/// 实体框架的数据访问层接口的构造工厂。/// </summary>public class DALFactory{//普通局部变量private static Hashtable objCache = new Hashtable();private static object syncRoot = new Object();private static DALFactory m_Instance = null;/// <summary>/// IOC的容器,可调用来获取对应接口实例。/// </summary>public IUnityContainer Container { get; set; }/// <summary>/// 创建或者从缓存中获取对应业务类的实例/// </summary>public static DALFactory Instance{get{if (m_Instance == null){lock (syncRoot){if (m_Instance == null){m_Instance = new DALFactory();//初始化相关的注册接口m_Instance.Container = new UnityContainer();//手工加载m_Instance.Container.RegisterType<ICityDAL, CityDAL>();m_Instance.Container.RegisterType<IProvinceDAL, ProvinceDAL>();}}}return m_Instance;}}

OK,通过上面的Unity,我们实现了对象的注入及使用个,具体的窗体调用代码如下所示。

        private void btnCity_Click(object sender, EventArgs e){DateTime dt = DateTime.Now;CityBLL bll = new CityBLL();var list = bll.GetAll();this.dataGridView1.DataSource = list;Console.WriteLine("花费时间:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds);}private void txtCityName_KeyUp(object sender, KeyEventArgs e){DateTime dt = DateTime.Now;CityBLL bll = new CityBLL();if(this.txtCityName.Text.Trim().Length > 0){var list = bll.GetAll(s => s.CityName.Contains(this.txtCityName.Text));this.dataGridView1.DataSource = list;                }else{var list = bll.GetAll();this.dataGridView1.DataSource = list;}Console.WriteLine("花费时间:{0}", DateTime.Now.Subtract(dt).TotalMilliseconds);}

我们可以得到具体的界面效果如下所示。

4、使用反射操作,在Unity容器动态注册接口对象

在上面的例子里面,不知道您是否注意到了,我们使用Unity的IOC容器的时候,注册的对象是指定的几个数据访问类。

     m_Instance.Container.RegisterType<ICityDAL, CityDAL>();m_Instance.Container.RegisterType<IProvinceDAL, ProvinceDAL>();

但这种有点类似硬编码的方式,在我们项目如果有大量的这些数据访问类,需要手工添加的话,那真不是一件雅观的事情。

如果代码能够根据接口和接口实现类,自动把我们所需要的接口对象注册进去,那该是多好的啊,可是能做到吗?能!

如果我们是在同一个程序集里面执行的话,那么我们通过反射操作,就可以从这个程序集里面获取对应的接口层(IDAL)和接口实现层(DAL)的对象,那么我们匹配它进行对象注入就可以了吧。

下面是我动态注册DAL对象的实现代码,如下所示。

        /// <summary>/// 使用Unity自动加载对应的IDAL接口的实现(DAL层)/// </summary>/// <param name="container"></param>private static void RegisterDAL(IUnityContainer container){Dictionary<string, Type> dictInterface = new Dictionary<string, Type>();Dictionary<string, Type> dictDAL = new Dictionary<string, Type>();Assembly currentAssembly = Assembly.GetExecutingAssembly();string dalSuffix = ".DAL";string interfaceSuffix = ".IDAL";//对比程序集里面的接口和具体的接口实现类,把它们分别放到不同的字典集合里foreach (Type objType in currentAssembly.GetTypes()){string defaultNamespace = objType.Namespace;if (objType.IsInterface && defaultNamespace.EndsWith(interfaceSuffix)){if (!dictInterface.ContainsKey(objType.FullName)){dictInterface.Add(objType.FullName, objType);}}else if (defaultNamespace.EndsWith(dalSuffix)){if (!dictDAL.ContainsKey(objType.FullName)){dictDAL.Add(objType.FullName, objType);}}}//根据注册的接口和接口实现集合,使用IOC容器进行注册foreach (string key in dictInterface.Keys){Type interfaceType = dictInterface[key];foreach (string dalKey in dictDAL.Keys){Type dalType = dictDAL[dalKey];if (interfaceType.IsAssignableFrom(dalType))//判断DAL是否实现了某接口{container.RegisterType(interfaceType, dalType);}}}}

有了这个利用反射动态注入对象的操作,我们在基类里面的实现就避免了硬编码的不便。

    /// <summary>/// 实体框架的数据访问层接口的构造工厂。/// </summary>public class DALFactory{//普通局部变量private static Hashtable objCache = new Hashtable();private static object syncRoot = new Object();private static DALFactory m_Instance = null;/// <summary>/// IOC的容器,可调用来获取对应接口实例。/// </summary>public IUnityContainer Container { get; set; }/// <summary>/// 创建或者从缓存中获取对应业务类的实例/// </summary>public static DALFactory Instance{get{if (m_Instance == null){lock (syncRoot){if (m_Instance == null){m_Instance = new DALFactory();//初始化相关的注册接口m_Instance.Container = new UnityContainer();//根据约定规则自动注册DALRegisterDAL(m_Instance.Container);//手工加载//m_Instance.Container.RegisterType<ICityDAL, CityDAL>();//m_Instance.Container.RegisterType<IProvinceDAL, ProvinceDAL>();}}}return m_Instance;}}

上面整个框架的优化过程,都是围绕着业务逻辑层进行的,最后我们实现了较好的动态对象的依赖注入,并给业务逻辑层对象提供了默认构造函数,让他们可以从IOC容器里面获取对象并创建。

但是我们看到,对于EDMX文件,我们只是把它放入了Entity的模块里面,也没有真正的对它如何处理,如果每次都需要使用这个edmx的文件生成操作,我依旧觉得开发效率比较低下,而且如果对于需要支持多个数据库如何处理呢?不可能在创建一个数据操作上下文吧,它们可以已经抽象化了,本身好像不是和具体数据库相关的,和数据库相关的只是它的配置关系而已啊。

这些问题留给下一篇继续对框架的演化处理吧,谢谢大家耐心的阅读,如果觉得有用,请继续推荐支持下,毕竟为了准备这个系列,我已经花了好多天的时间,从各个方面持续优化整个仓储模式的实体框架,留下一个个版本的Demo来整理博客的。

1. 顺序
      当创建一个派生类的对象时,系统首先自动创建一个基类对象,也就是说,在调用派生类构造函数创建派生类对象之前,系统首先调用基类的构造函数创建基类对象。当派生类对象生命期结束时,首先调用派生类的析构函数,然后调用基类的析构函数。简而言之,就是说,构造函数:基类->派生类。析构函数:派生类->基类。
这个我们完全可以通过一个小程序来说明:

//通过输出就可以看出在创建派生类对象b1时各个函数的调用顺序了
#include <iostream>
using namespace std;
class A
{
public:A(){cout<<"A(Based Class) constructor is called"<<endl;}~A(){cout<<"A(Based Class) destructor is called"<<endl;}
};
class B:public A
{
public:B(){cout<<"B(Derived Class) constructor is called"<<endl;}~B(){cout<<"B(Derived Class) destructor is called"<<endl;}
};int main()
{B b1;return 0;
}

2. 通过派生类的构造函数调用基类的构造函数有两种方式,隐式和显式两种。所谓隐式方式就是在派生类的构造函数中不指定对应的基类的构造函数,这个时候调用的是基类的默认构造函数(即含有默认参数值或不带参数的构造函数)。而所谓显式方式,就是在派生类的构造函数中指定要调用的基类的构造函数,并将派生类构造函数的部分参数值传递给基类构造函数。注:除非基类有默认的构造函数,否则必须采用显式调用方式

下面分别给出一个隐式和显式调用的例子:

#include <iostream>
using namespace std;class A
{
public:A(int x = 0,int y = 0){a = x;b = y;}
private:int a;int b;
};
//基类A有默认的构造函数,可以隐式调用
class B:public A
{
public:B(int z = 0){c = z;}
private:int c;
};int main()
{B b1;return 0;
}

按 Ctrl+C 复制代码

转载于:https://www.cnblogs.com/sjqq/p/7702016.html

Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2)相关推荐

  1. Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)

    很久没有写博客了,一些读者也经常问问一些问题,不过最近我确实也很忙,除了处理日常工作外,平常主要的时间也花在了继续研究微软的实体框架(EntityFramework)方面了.这个实体框架加入了很多特性 ...

  2. 依赖注入框架Dagger2详解(一),依赖注入和控制反转的深入理解

    IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序,而Dagger2框架是依赖注入思想践行者的优秀代表. 依赖注入框架Dagger2详解(一), ...

  3. Spring框架学习笔记(1) ---[spring框架概念 , 初步上手使用Spring , 控制反转 依赖注入初步理解 ]

    spring官网 -->spring官网 spring5.3.12–>spring-framework 在线文档 --> Spring 5.3.12 文章目录 1.Spring概论 ...

  4. 依赖注入(Dependency Injection)框架是如何实现的?

    点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/fuzhongmin05/ article/details/109572151 当创建对象是一件庞大又复杂的大工程的时候,我们一 ...

  5. java 什么时候依赖注入_玩框架java依赖注入 – 何时使用单例

    So I am wondering, should I be using singleton objects as the examples seem to imply? If this is the ...

  6. Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)

    在前面几篇关于Entity Framework 实体框架的介绍里面,已经逐步对整个框架进行了一步步的演化,以期达到统一.高效.可重用性等目的,本文继续探讨基于泛型的仓储模式实体框架方面的改进优化,使我 ...

  7. 自定义Unity对象生命周期管理集成ADO.NET Entity Framework

    在Unity中,从Unity 取得的实例为 Transient.如果你希望使用多线程方式,就需要在组成时使用lifecycle参数,这时候取出的组件就不再是同一个了.在Unity IOC中,它支持我们 ...

  8. 全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

    在开发涉及到数据库的程序时,常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况,这时就需要进行数据库迁移. 实现数据库迁移有很多种办法,从手动管理各个版本的ddl脚本,到实现自己的mig ...

  9. 【转】学习Entity Framework 中的Code First

    这是上周就写好的文章,是在公司浩哥的建议下写的,本来是部门里面分享求创新用的,这里贴出来分享给大家. 最近在对MVC的学习过程中,接触到了Code First这种新的设计模式,感觉很新颖,并且也体验到 ...

最新文章

  1. python build-in function
  2. yoman不压缩html,使用Yeoman构建vuejs
  3. 计算机的特别及应用,[计算机软件及应用]Excel使用技巧-非常全.doc
  4. 关于最佳线程数的计算的准确理解
  5. STM32与SHT1X温湿度传感器通讯
  6. 飞秋文件传输模拟实现代码
  7. c语言比两个人年龄大小,c语言结构体
  8. 公司使用 Qt 到底要不要付费?| 博文精选
  9. C# DirectX.AudioVideoPlayback音频视频播放
  10. mysql hive 建表语句_关于Mysql元数据如何生成Hive建表语句注释脚本
  11. LeetCode:Confusing Number II
  12. JAVA学习日志 关于调用方法、生成对象的例子。还是用数字卦程序修改
  13. [NOI2008]糖果雨
  14. 空间数据库Topic推荐-AMiner
  15. 如何将小鹤单字挂接到搜狗输入法
  16. 618电商大数据分析可视化报告
  17. python阶梯图_不会你还不懂怎么用Python制图吧?师兄教你如何学会绘制漂亮的阶梯图...
  18. 听秋夜虫鸣,与自己对话
  19. 视频号9大变现模式,总有一个适合你
  20. PHP常见三种设计模式:单例、工厂、观察者

热门文章

  1. Window下JDK、Tomcat、eclipse安装与配置
  2. 【vijos P1914】【codevs 3904】[NOIP2014 普及组T4]子矩阵(dfs+状压dp)
  3. 第十一节(单例模式初步、类的继承)
  4. c# winform WebBrower 控件中右键获取控件坐标
  5. JavaScript学习(五十)—hasOwnProperty属性和in关键字
  6. 2018年计算机二级知识点,2018年计算机二级考试公共基础知识点:栈及其基本运算...
  7. diybox路由器设置教程_家庭无线路由器怎么设置 家庭无线路由器设置教程【详细方法】...
  8. 人能为赚钱吃多少苦?
  9. 纸质书,电子书,你会选择通过哪一种途径学习?
  10. 你们一般持有几支基金?怎么管理的?