在本系列的第一篇随笔《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 > )
{
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(); //根据约定规则自动注册DAL
RegisterDAL(m_Instance.Container); //手工加载
//m_Instance.Container.RegisterType<ICityDAL, CityDAL>();
//m_Instance.Container.RegisterType<IProvinceDAL, ProvinceDAL>();
}
}
}
return m_Instance;
}
}

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

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

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

这个系列文章索引如下:

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

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

Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)

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

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

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

  2. 《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11  测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经 ...

  3. DI 依赖注入之StructureMap框架

    DI  依赖注入之StructureMap框架 一.简叙: structureMap只是DI框架中的其中之一. 二.安装及使用: 1.懒人方法: 使用MVC5项目时,可以直接在nuget程序包中安装S ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (23) -----第五章 加载实体和导航属性之预先加载与Find()方法

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-2  预先加载关联实体 问题 你想在一次数据交互中加载一个实体和与它相关联实体. ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (24) ------ 第五章 加载实体和导航属性之查询内存对象

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-4  查询内存对象 问题 你想使用模型中的实体对象,如果他们已经加载到上下文中, ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (25) ------ 第五章 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-5  加载完整的对象图 问题 你有一个包含许多关联实体的模型,你想在一次查询中, ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-7  在别的LINQ查询操作中使用Include()方法 问题 你有一个LINQ ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-9  关联实体过滤和排序 问题 你有一实体的实例,你想加载应用了过滤和排序的相关 ...

随机推荐

  1. Java多线程系列--“JUC锁”05之 非公平锁

    概要 前面两章分析了"公平锁的获取和释放机制",这一章开始对“非公平锁”的获取锁/释放锁的过程进行分析.内容包括:参考代码获取非公平锁(基于JDK1.7.0_40)释放非公平锁(基 ...

  2. 浅谈Excel开发:五 Excel RTD函数

        上文介绍了Excel中的UDF函数,本文介绍一下同样重要的RTD函数.从Excel 2002开始,Excel引入了一种新的查看和更新实时数据的机制,即real-time data简称RTD函数 ...

  3. kafka的一些认识

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com   近来无事研究了一下kafka,并且用golang连接kafka做了producer和consumer的简单测试 ...

  4. django开发个人简易Blog——数据模型

    提到数据模型,一定要说一下MVC,MVC框架是现代web开发中最流行的开发框架,它将数据与业务逻辑分开,减小了应用之间的高度耦合.个人非常喜欢MVC开发框架,除了具有上述特性,它使得web开发变得非常 ...

  5. NanoProfiler - 适合生产环境的性能监控类库 之 大数据篇

    上期回顾 上一期:NanoProfiler - 适合生产环境的性能监控类库 之 基本功能篇 上次介绍了NanoProfiler的基本功能,提到,NanoProfiler实现了MiniProfiler欠 ...

  6. UStore-自定义JDF文件格式输出

    系统默认的JDF输出不能满足我们的需求,往往不同的供应商输出不同要求的JDF格式.这里我们开始介绍ustore的自定义JDF输出 1.先屏蔽掉默认的JDF格式输出 我们进入Tigger来设置ustro ...

  7. Java-数组练习4

    16.按要求编写Java应用程序.编写一个名为Test的主类,类中只有一个主方法: 在主方法中定义一个大小为50的一维整型数组,数组名为x,数组中存放着{1, 3,5,…,99}输出这个数组中的所有元 ...

  8. Visual Studio 2012系统环境变量设置(命令行)

    方法1.运行脚本vsvars32.bat:D:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Tools\vsvars32.bat ...

  9. Java项目——模拟电话薄联系人增删改查

    该项目模拟了电话本记录联系人的业务功能,用来练习对数据库的增删改查等操作. 菜单类:Menu -- 用来封装主菜单和个选项的子菜单 Person类: Person--联系人的实体类 TelNoteRe ...

  10. JavaScript开发的技巧

    1. 使用===取代==    ==和!=操作符会在需要的情况下自动转换数据类型.但===和!==不会,它们会同时比较值和数据类型,这也使得它们要比==和!=快. "){ //速度慢 } & ...