c#框架,Entity Framework 實體框架的形成之旅--利用Unity對象依賴注入優化實體框架(2)

 2023-10-18 阅读 31 评论 0

摘要:在本系列的第一篇隨筆《Entity Framework 實體框架的形成之旅--基于泛型的倉儲模式的實體框架(1)》中介紹了Entity Framework 實體框架的一些基礎知識,以及構建了一個簡單的基于泛型的倉儲模式的框架,例子也呈現了一個實體框架應用的雛形,本篇繼續介紹這

在本系列的第一篇隨筆《Entity Framework 實體框架的形成之旅--基于泛型的倉儲模式的實體框架(1)》中介紹了Entity Framework 實體框架的一些基礎知識,以及構建了一個簡單的基于泛型的倉儲模式的框架,例子也呈現了一個實體框架應用的雛形,本篇繼續介紹這個主題,繼續深化介紹Entity Framework 實體框架的知識,以及持續優化這個倉儲模式的實體框架,主要介紹業務邏輯層的構建,以及利用Unity和反射進行動態的對象注冊。

1、EDMX文件位置的調整

我們從上篇例子,可以看到這個隨筆介紹的倉儲模式的實體框架結構如下所示。

c#框架、但實際上上篇隨筆的例子是有點理想化的了,因為我們知道,【ADO.NET實體數據模型】生成的EDMX文件實質上自動生成了數據訪問的上下文SqlserverContext,以及幾個表的實體類,具體的效果如下所示。

我們理想化的把它放到DAL目錄,Entity目錄下,實際上是不可以的,至少是有沖突的。

那么我們應該如何處理,才能比較合理的處理這些自動生成的內容呢?另外我們已經把它上升了一層到業務層,具體的BLL分層如何處理數據訪問對象的呢,通過什么方式構建數據訪問對象?帶著這些問題,我們再來一步步分析這個框架的內容。

實體框架中的概念層、為了給實體類友好的名稱,我們順便把表名的前綴移除了,如EDMX的圖形如下所示。

為了比較好的利用EDMX文件的代碼生成,我們把這個文件整體性的移動到了Entity目錄下,如下所示。

framework框架,這樣相當于把數據訪問的上下文,以及實體類的代碼全部移動到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();}}
復制代碼

JAVA對象框架、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);}
復制代碼

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

unity開源游戲框架??

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

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/146355.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息