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

简介:

在本系列的第一篇随笔《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();

                            //根据约定规则自动注册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),如需转载请自行联系原博主。



目录
相关文章
|
5月前
|
大数据 API 图形学
Unity优化——批处理的优势
Unity优化——批处理的优势
152 0
|
5月前
|
存储 人工智能 Java
Unity优化——脚本优化策略4
Unity优化——脚本优化策略4
101 0
|
5月前
|
安全 Java 图形学
Unity3D 导出的apk进行混淆加固、保护与优化原理(防止反编译)
Unity3D 导出的apk进行混淆加固、保护与优化原理(防止反编译)
74 0
|
3月前
|
存储 设计模式 监控
运用Unity Profiler定位内存泄漏并实施对象池管理优化内存使用
【7月更文第10天】在Unity游戏开发中,内存管理是至关重要的一个环节。内存泄漏不仅会导致游戏运行缓慢、卡顿,严重时甚至会引发崩溃。Unity Profiler作为一个强大的性能分析工具,能够帮助开发者深入理解应用程序的内存使用情况,从而定位并解决内存泄漏问题。同时,通过实施对象池管理策略,可以显著优化内存使用,提高游戏性能。本文将结合代码示例,详细介绍如何利用Unity Profiler定位内存泄漏,并实施对象池来优化内存使用。
188 0
|
1月前
|
测试技术 C# 图形学
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
64 4
|
1月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
118 3
|
2月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
79 0
|
2月前
|
开发者 图形学 UED
深度解析Unity游戏开发中的性能瓶颈与优化方案:从资源管理到代码执行,全方位提升你的游戏流畅度,让玩家体验飞跃性的顺滑——不止是技巧,更是艺术的追求
【8月更文挑战第31天】《Unity性能优化实战:让你的游戏流畅如飞》详细介绍了Unity游戏性能优化的关键技巧,涵盖资源管理、代码优化、场景管理和内存管理等方面。通过具体示例,如纹理打包、异步加载、协程使用及LOD技术,帮助开发者打造高效流畅的游戏体验。文中提供了实用代码片段,助力减少内存消耗、提升渲染效率,确保游戏运行丝滑顺畅。性能优化是一个持续过程,需不断测试调整以达最佳效果。
66 0
|
4月前
|
存储 图形学
【unity小技巧】unity事件系统创建通用的对象交互的功能
【unity小技巧】unity事件系统创建通用的对象交互的功能
43 0
|
4月前
|
开发工具 图形学 Android开发
【推荐100个unity插件之6】利用Photon PUN2框架最快最简单实现多人在线游戏
【推荐100个unity插件之6】利用Photon PUN2框架最快最简单实现多人在线游戏
96 0