一, 普通架构中存在的问题
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);
- }
- View Code
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 或者数据库, 最终调用结构为:
如果我哪里写的有问题或者我说的不够清楚或者你有疑问的话, 欢迎留言
来源: https://www.cnblogs.com/ckka/p/11411632.html