一、普通架構中存在的問題
StudentDB數據庫,包含一張StudentInfoTB表,結構如下:
s_id int primary key identity(1,1), s_name Nvarchar(10) not null, s_age int check(s_age >10 and s_age<30), s_sex bit not null
先來看一下普通的架構的問題所在:
調用關系:
dal層代碼只是通過SqlHelper簡單的操作一下數據庫,就不展示了。
StudentInfo的bll層代碼,實例化了一個dal層對象,並且每個方法返回對應的方法:

private StudentInfoADODal dal = new StudentInfoADODal(); public List<StudentInfoModel> Select() { return dal.Select(); } public int Update(StudentInfoModel siModel) { return dal.Update(siModel); } public int Delete(int id) { return dal.Delete(id); } public int Add(StudentInfoModel siModel) { return dal.Add(siModel); } public StudentInfoModel Get(int id) { return dal.Get(id); }
ui層調用:
static void Main(string[] args) { Display(); Console.ReadKey(); } static void Display() { //實例化bll層對象 StudentInfoBll bll = new StudentInfoBll(); //接收返回值 List<StudentInfoModel> lstModel = bll.Select(); //輸出標題 Console.WriteLine($"編號\t姓名\t年齡\t性別"); //循環輸出 foreach (StudentInfoModel item in lstModel) { Console.WriteLine($"{item.s_id}\t{item.s_name}\t{item.s_age}\t{item.s_sex}"); } }
輸出:
一切看起來都沒什么問題,可是如果后續邏輯復雜起來了,並且Dal層對象有變動的話,我們是不是需要在Bll層修改實例化的Dal層對象,而且一但更改,所有位置的Dal層對象都會修改,現在代碼很少,東西也不復雜,可是如果代碼復雜了,修改的話就不是太方便了。
首先我們應該想到的是工廠模式,因為如果Dal層有變動的話,我們只需要修改工廠模式中返回的對象就行了,但是如果使用簡單工廠模式的話,返回值是個問題,Dal層改變的話,返回值肯定也會發生改變,到時候還是要對bll層接收對象修改類型。
二、抽象工廠+IDal
所以我們應該考慮使用抽象工廠,既然要用抽象工廠,那么就必須有一個具體的父類或者接口,但是Dal層並沒有繼承任何父類或實現任何接口(Object除外),所以我們應該抽象出一個IDal層,使得所有的Dal層都要去實現IDal層,然后用IDal層的接口作為返回值,返回給Bll層,后續如果在要修改Dal層的話,只要Dal層實現了IDal層我們是不是就不用在對Bll層做任何修改了。
接下來先創建IDal層:
然后抽象出IStudentInfoDal接口:
public interface IStudentInfoDal { List<StudentInfoModel> Select(); int Update(StudentInfoModel siModel); int Delete(int id); int Add(StudentInfoModel siModel); StudentInfoModel Get(int id); }
StudentInfoADODal層實現IStudentDal接口:
下面就是使用工廠模式來進行創建對象,先創建工廠層和DalFactory類:
public class DalFactory { public static IStudentInfoDal CreateStudentInfoInstance() { return new StudentInfoADODal(); } }
Bll層調用的話直接聲明接口,然后通過工廠模式來獲取Dal層對象:
private IStudentInfoDal dal = DalFactory.CreateStudentInfoInstance();
這下如果后續需要更改的話,直接就更改工廠模式這一個地方就行了,其他地方就不用做更改了。
現在看一下調用關系,箭頭是引用關系
可以發現,Dal層並沒有做到完全獨立起來,Factory層還是在引用Dal層,更換數據庫的話,還是要重新添加引用,重新修改工廠模式中的代碼。小項目不分層都是可以的,如果項目很復雜,做任何事情之前都要考慮周到,任何細節都要處理好,所以就想辦法把Dal層完全獨立起來
三、反射+App.config實現抽象工廠
反射就是能夠動態加載程序集,不需要添加對程序集的引用,就可以獲取程序集內部的結構(屬性、方法),可以實現動態創建對象,調用對象的方法,為屬性賦值等操作。所以,在創建Dal層對象時,我們可以考慮使用反射來創建。
反射所在命名空間:System.Reflection; 其實就是先將dll文件給加載到Assembly對象中,然后通過Assembly對象創建dll文件中的對象(反射還有其他的幾個常用的對象Type、Activator、PropertyInfo...)
使用反射創建dal層對象:
public static IStudentInfoDal CreateStudentInfoInstance() { //使用Assembly來加載程序集 Assembly assembly = Assembly.Load("CKKA.ADODal"); //通過assembly對象來創建一個StudentInfoADODal實例 //必須是完整的類型名稱 類型所在命名空間+“.”+類名 Object siDal = assembly.CreateInstance("CKKA.ADODal.StudentInfoADODal"); return siDal as IStudentInfoDal; }
然后右鍵CKKA.ADODal-->屬性-->最左側生成-->下方輸出路徑改為Ui/bin/debug或者Ui/bin(具體可以自己打開文件夾下看那個目錄下有dll文件),詳情看這篇帖子https://www.cnblogs.com/ckka/p/11331037.html
現在運行該程序是可以反射成功的
我們把將抽象工廠再次改造一下,將CKKA.ADODal和后綴ADODal寫在App.config的appSettings節點下(web項目寫在web.config)
<add key="AssemblyName" value="CKKA.ADODal"/> <add key="Suffix" value="ADODal"/>
抽象工廠改進為(需要為Factory添加System.Configuration程序集和命名空間的引用):
public class DalFactory { //從配置文件中讀取AssemblyName(程序集名稱)和Suffix(Dal層擴展名) private static String AssemblyName = ConfigurationManager.AppSettings["AssemblyName"]; private static String Suffix = ConfigurationManager.AppSettings["Suffix"]; //每個方法都調用此方法來創建對象 private static Object CreateInstance(String TypeName) { return Assembly.Load(AssemblyName).CreateInstance(TypeName); } public static IStudentInfoDal CreateStudentInfoInstance() { //拼接類型名稱 String TypeName = $"{AssemblyName}.StudentInfo{Suffix}"; //創建實例 return CreateInstance(TypeName) as IStudentInfoDal; } }
以后我們如果需要更換數據庫的話,只需要修改配置文件,操作數據庫的Dal實現IDal接口,並且bin目錄下有dll文件就行了,不需要更改任何代碼。
由於沒有其他數據庫,我們就用EF操作數據庫來測試一下:
將CKKA.EFDal的生成路徑更改一下,然后修改配置文件為:
然后運行:
可以發現,完全不用改任何代碼,就可以做到更換一整個Dal或者數據庫,最終調用結構為:
如果我哪里寫的有問題或者我說的不夠清楚或者你有疑問的話,歡迎留言