LINQ to SQL活学活用(4):监视你的一举一动
2008-11-12 22:40 李永京 阅读(6573) 评论(11) 编辑 收藏 举报引入
我们看看数据访问层有什么地方可以扩展呢?想到这个实现了吗?我需要时刻监视所有数据的一举一动,是谁创建它的?何时创建的?是谁修改它的?何时修改的?你想到这个问题怎么实现呢?使用开源日志记录框架例如log4net,或者自己写一份单独的实现利用IoC动态切入到你的程序中?没有必要!看看用LINQ to SQL如何实现这个功能吧!
注意:这是我第一次分析设计,我只想通过这个系列来讨论学习设计方面的东西,当然很多思想还不成熟或者有错误,只是希望通过这个系列来学习构架设计方面的东西,希望大牛们指点,大家拍砖头!
系列文章导航
系列参考代码下载:LINQ to SQL
改进
这也可以运用GoF23中的观察者模式,让多个观察者对象(对象创建、修改)同时监听某一个主题对象(这里就是数据访问对象Customer)。这个主题对象Customer在状态发生变化时,会通知所有观察者对象(对象创建、修改),使它们能够自动更新自己,就可以实现监视Customer的一举一动了。
我们来实现这个功能,先看看这篇完成的整体架构。
数据访问层
1.数据访问基类
我们为整个数据访问对象定义一个公共基类,首先定义定义数据访问基类接口,让抽象数据访问基类实现这个接口,这个基类用于存放数据访问对象共有的属性,设置为抽象的。在我们的系统中,并非只有Customer这个数据访问对象,将来肯定要增加另外很多的了,这样如果将来增加另外的数据访问对象,只需继承这个基类重写属性即可。
Step1:定义数据访问基类接口:
public interface IDataAccessBase { int Id { get; set; } DateTime CreatedDate { get; set; } DateTime ModifiedDate { get; set; } string CreatedBy { get; set; } string ModifiedBy { get; set; } }
Step2:定义抽象的数据访问基类实现这个接口:针对抽象编程,减少了与具体类的耦合。
public abstract class DataAccessBase : IDataAccessBase { public abstract int Id { get; set; } public abstract DateTime CreatedDate { get; set; } public abstract DateTime ModifiedDate { get; set; } public abstract string CreatedBy { get; set; } public abstract string ModifiedBy { get; set; } }
2.数据访问对象Customer
既然在数据访问基类中定义了抽象的Id、CreatedBy、CreatedDate、ModifiedBy、ModifiedDate等属性,那么我们改修改数据访问对象Customer的属性了,看看截图
修改Customer类,增加了CreatedBy、CreatedDate、ModifiedBy、ModifiedDate属性,并把CustomerId换为Id,需要特别声明的是,这些字段属性的修饰符必须修改为重写“override"。
修改数据访问对象Customer接口,继承数据访问基类接口IDataAccessBase。
public interface ICustomer : IDataAccessBase { int Id { get; set; } string FirstName { get; set; } string LastName { get; set; } }
修改数据访问对象Customer分部类,继承数据访问基类接口IDataAccessBase和数据访问对象Customer接口,这时数据访问对象Customer一共七个属性了。
public partial class Customer : DataAccessBase, ICustomer { }
数据访问对象Customer修改完成,如果我们要添加另外的数据访问对象,很容易的去扩展,也很容易的重写自己的属性。
3.数据访问对象DataContext
定义数据访问对象DataContext分部类DataAccessEntitiesDataContext,用于存放一些操作对象的公共方法,例如是谁创建数据的?何时创建这个数据的?是谁修改数据的?何时修改的?我们可以重写LINQ to SQL内置的方法实现。
DataContext为每个实体都定义了以下三个分部方法:InsertEntityName()、UpdateEntityName()、DeleteEntityName()。
我们在OR设计器中添加了Customer类,内置定义了三个分部方法,就是:
partial void InsertCustomer(Customer instance); partial void UpdateCustomer(Customer instance); partial void DeleteCustomer(Customer instance);
我们就重写上面三个方法实现观察的作用,在重写上面的方法的时需要调用ExecuteDynamicInsert()、ExecuteDynamicUpdate()、ExecuteDynamicDelete()方法。
- ExecuteDynamicInsert()方法:在插入重写方法中调用,以向LINQ to SQL重新委托生成和执行插入操作的动态SQL的任务。
- ExecuteDynamicUpdate()方法:在更新重写方法中调用,以向LINQ to SQL重新委托生成和执行更新操作的动态SQL的任务。
- ExecuteDynamicDelete()方法:在删除重写方法中调用,以向LINQ to SQL重新委托生成和执行删除操作的动态SQL的任务。
我们重写新建一个分部类DataAccessEntitiesDataContext.cs,在这个分部类中重写上面三个分部方法的具体实现。这篇我只需重写InsertCustomer方法(插入Customer时触发)和UpdateCustomer(更新Customer时触发)两个方法,为了程序可以复用,我们把插入数据访问对象时、修改数据访问对象时记录信息封装成两个方法实现。
Step1:创建数据访问对象实现记录创建者(这里获取机器当前用户名)和创建时间,由于修改字段不为空,这里默认使用创建时的信息。
private void InsertDataAccessBase(DataAccessBase instance) { //创建对象时(记录创建者和时间) instance.CreatedBy = WindowsIdentity.GetCurrent().Name; instance.CreatedDate = DateTime.Now; //修改对象时(记录修改者和时间) instance.ModifiedBy = WindowsIdentity.GetCurrent().Name; instance.ModifiedDate = DateTime.Now; //在插入重写方法中调用 //向LINQ to SQL重新委托生成和执行插入操作的动态SQL的任务 ExecuteDynamicInsert(instance); }
Step2:修改数据访问对象实现记录修改者(这里获取机器当前用户名)和修改时间。
private void UpdateDataAccessBase(DataAccessBase instance) { //修改对象时(记录修改者和时间) instance.ModifiedBy = WindowsIdentity.GetCurrent().Name; instance.ModifiedDate = DateTime.Now; ExecuteDynamicUpdate(instance); }
Step3:重写InsertCustomer()分部方法,调用InsertDataAccessBase()方法即可。
partial void InsertCustomer(Customer instance) { InsertDataAccessBase(instance); }
Step4:重写UpdateCustomer()分部方法,调用UpdateDataAccessBase()方法即可。
partial void UpdateCustomer(Customer instance) { UpdateDataAccessBase(instance); }
如果系统又要增加别的数据访问对象的话,如果想实现记录功能只需要调用InsertDataAccessBase()、UpdateDataAccessBase()方法即可,这样就提高了代码的复用性。
4.数据访问对象Customer外观Facade
这个同上一篇一样,无需任何改动,外观Facade依赖关系为Customer接口,这里Customer接口没有变化哦!
单元测试层
以上所有的工作离不开测试!只有通过测试,才可以验证我们程序的正确性!好了,我们开始,既然我们修改了数据访问对象,那么也要修改对应的测试了!
1.创建数据访问对象测试
测试创建数据访问对象,比较创建者是否同机器用户名一致!
[Test] public void UpdateTest() { ICustomer newCustomer = CreateAndSaveNewCustomer("YJing", "Lee"); Assert.AreNotEqual(0, newCustomer.Id); Assert.AreEqual("YJing", newCustomer.FirstName); Assert.AreNotEqual(DateTime.MinValue, newCustomer.CreatedDate); Assert.AreNotEqual(DateTime.MinValue, newCustomer.ModifiedDate); Assert.IsNotNull(newCustomer.CreatedBy); Assert.IsNotNull(newCustomer.ModifiedBy); //获取机器当前用户名 string currentUserName = WindowsIdentity.GetCurrent().Name; //比较创建者和修改者 Assert.AreEqual(currentUserName, newCustomer.CreatedBy); Assert.AreEqual(currentUserName, newCustomer.ModifiedBy); }
测试输出如下
分析一下:先验证数据库是否存在,存在删除,然后重新创建一个新的数据库架构和Customer表,这个表有7个字段。我们向数据库中插入了一条数据,系统自动增加了创建者、创建时间、修改者、修改时间等信息。
2.修改数据访问对象测试
既然我们增加了这个功能,那么我们来测试验证一下:先插入一条数据,然后等待3秒,更新这个对象,看看修改时间是否是我们预期的变化值。
[Test] public void ModifyCustomerUpdatesModifiedDateTest() { ICustomer tempCustomer = CreateAndSaveNewCustomer("YJing", "Lee"); DateTime originalLastModifiedDate = tempCustomer.ModifiedDate; Thread.Sleep(1500); tempCustomer.FirstName = "CnBlogs"; Facade.UpdateCustomer(tempCustomer); Assert.AreNotEqual(originalLastModifiedDate, tempCustomer.ModifiedDate); }
测试输出如下:
这里省略了创建数据库的过程,第一句插入一个对象,第二句更新一个对象,仔细看看其修改时间是否变化了。
大家再测试下原先的几个测试吧!
结语
这篇通过重写分部方法的方法,来实现了非常酷的功能,时刻监视着数据。也从设计的角度知道针对抽象编程,减少了与具体类的耦合。在这里我们很少和Customer类打交道。