• LINQ to SQL活学活用(4):监视你的一举一动


    Creative Commons License

    本文基于署名 2.5 中国大陆许可协议发布。作者:李永京(http://www.cnblogs.com/lyj/),原文地址:(请在这里替换原文地址URL)。

    引入

    我们看看数据访问层有什么地方可以扩展呢?想到这个实现了吗?我需要时刻监视所有数据的一举一动,是谁创建它的?何时创建的?是谁修改它的?何时修改的?你想到这个问题怎么实现呢?使用开源日志记录框架例如log4net,或者自己写一份单独的实现利用IoC动态切入到你的程序中?没有必要!看看用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重写属性

    修改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类打交道。

  • 相关阅读:
    .net序列化和反序列化(一)——自动序列化
    在Sql Server 2005使用公用表表达式CTE简化复杂的查询语句
    使用JQuery与iframe交互
    FCKeditor自定义工具栏和定义多个工具栏
    FCKeditor自定义非空验证
    PHP5.3.6的IIS配置
    Linux下缓存服务器的应用
    PHP采集程序中常用的函数
    关于PHP5.3.x和Zend Optimizer(Zend Guard Loader),以及shopex4.8.5安装的问题
    SQLserver数据库还原出现错误112(磁盘空间不足)的解决办法
  • 原文地址:https://www.cnblogs.com/wonder223/p/2747443.html
Copyright © 2020-2023  润新知