65.9K
CodeProject 正在变化。 阅读更多。
Home

工作单元设计模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (103投票s)

2013 年 4 月 22 日

CPOL

7分钟阅读

viewsIcon

533562

downloadIcon

7192

本文将讨论工作单元设计模式。

 

目录

您是设计模式新手吗?

如果您是设计模式的完全新手,可以从下面的 CodeProject 文章开始:

工作单元设计模式有什么用?

工作单元设计模式做两件重要的事情:首先,它维护内存中的更新;其次,它将这些内存中的更新作为一个事务发送到数据库。

因此,为了实现上述目标,它需要经历两个步骤

  • 它在内存中维护了一个业务对象列表,这些对象在事务期间已更改(插入、更新或删除)。
  • 一旦事务完成,所有这些更新将作为**一个大的工作单元**一次性持久化到数据库中。

软件应用程序中的“工作”和“单元”是什么?

“工作”的简单定义意味着**_执行某些任务_**。从软件应用程序的角度来看,**工作**无非是插入、更新和删除数据。例如,假设您有一个应用程序,它将客户数据维护在数据库中。

因此,当您在数据库中添加、更新或删除客户记录时,它就是一个单元。简单来说,等式是:

1 customer CRUD = 1 unit of work

其中 CRUD 代表对单个客户记录的创建、读取、更新和删除操作。

逻辑事务!= 物理 CRUD 

当涉及到实际场景时,我们上一节讨论的等式会发生很大变化。现在考虑以下场景,每个客户可以有多个地址。那么许多行将成为 1 个工作单元。

例如,您可以在下图中看到客户“Shiv”有两个地址,因此在下面的场景中,等式是:

3 Customer CRUD = 1 Logical unit of work

简单来说,所有三条记录的事务要么都成功,要么都失败。它应该是**原子性的**。换句话说,很多 CRUD 操作都可能等于 1 个工作单元。

所以这可以通过使用简单的事务来实现吗?

许多开发人员可能会得出结论,可以通过在一个事务中启动所有 CRUD 操作来满足上述要求。当然,它在底层使用数据库事务(即 `TransactionScope` 对象)。但工作单元远不止简单的数据库事务,它只发送更改,而不是所有行到数据库。

让我更详细地向您解释。

假设您的应用程序从数据库中检索三条记录。但它只修改了两条记录,如下图所示。因此,只有修改过的记录被发送到数据库,而不是所有记录。这优化了物理数据库访问次数,从而提高了性能。

简单来说,工作单元的最终等式是:

1 Unit of work = Modified records in a transaction

那么我们如何实现这一点呢?

为了实现这一点,我们首先需要一个内存中的集合。在这个集合中,我们将添加所有的业务对象。现在,当应用程序中发生事务时,它们将在该内存集合中被跟踪。

一旦应用程序完成所有操作,它将把这些已更改的业务对象以“一个事务”发送到数据库。换句话说,它们要么全部提交,要么全部失败。

工作单元的 C# 示例代码

步骤 1:为业务对象创建通用接口 (IEntity)

归根结底,工作单元不过是一个维护和跟踪业务对象更改的集合。现在在一个应用程序中,我们可以有许多不同类型的业务对象。例如,您可以有客户、供应商、账户等。为了使该集合在任何业务对象之间可重用,让我们将所有业务对象概括为“实体”。

通过这种方法,我们可以使这个集合与任何业务对象可重用,从而避免任何重复代码。

因此,第一步是创建一个名为 `IEntity` 的通用接口,它代表我们项目中的业务对象。

这个 `IEntity` 接口将有一个 `ID` 属性和方法(插入、更新、删除和加载),这将帮助我们对业务对象执行 CRUD 操作。`ID` 属性是一个唯一的数字,它帮助我们唯一地标识数据库中的记录。

public interface IEntity
{
    int Id { set; get; }
    void Insert();
    void Update();
    List<IEntity> Load();
}  

步骤 2:实现 IEntity 接口

下一步是在所有业务对象中实现 `IEntity`。例如,您可以在下面看到一个简单的 `Customer` 业务对象,它实现了 `IEntity` 接口。它还实现了所有 CRUD 操作。您可以看到它调用了数据访问层来实现插入、更新和加载操作。

public class Customer : IEntity
{
    private int _CustomerCode = 0;
    public int Id
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    private string _CustomerName = "";
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
    public void Insert()
    {
        DataAccess obj = new DataAccess();
        obj.InsertCustomer(_CustomerCode, CustomerName);
    }
    public  List<IEntity> Load()
    {
        DataAccess obj = new DataAccess();
        Customer o = new Customer();
        SqlDataReader ds = obj.GetCustomer(Id);
        while (ds.Read())
        {
            o.CustomerName = ds["CustomerName"].ToString();
        }
        List<IEntity> Li = (new List<Customer>()).ToList<IEntity>();
        Li.Add((IEntity) o);
        return Li;
    }
    public void Update()
    {
        DataAccess obj = new DataAccess();
        obj.UpdateCustomer(_CustomerCode, CustomerName);
    }
} 

为了避免混淆,本文中未显示数据访问代码。您可以随时下载文章随附的完整代码。

步骤 3:创建工作单元集合

下一步是创建工作单元集合类。下面是我们创建的一个名为 `SimpleExampleUOW` 的简单类。

这个类定义了两个泛型集合作为类级别变量:`Changed` 和 `New`。`Changed` 泛型集合将存储用于更新的实体。`New` 泛型集合将包含新的业务实体/记录。

请注意,在此演示中,我没有实现删除功能。我把它留给您作为家庭作业 Smile | <img src=

public class SimpleExampleUOW
{
    private List<T> Changed = new List<T>();
    private List<T> New = new List<T>();
    …..
    …..
}  

我们还实现了 `Add` 和 `Load` 方法,分别加载 `New` 和 `Changed` 集合。如果对象通过 `Add` 方法输入,则将其添加到 `New` 集合中。如果它是从数据库加载的,则将其加载到 `Changed` 集合中。

public void Add(T obj)
{
    New.Add(obj);
}
public  void Load(IEntity o)
{
    Changed  = o.Load() as List<T>;
}

现在,集合中更新的所有这些更改也需要**一次性**发送到数据库。为此,我们还创建了一个 `Commit` 函数,它遍历 `New` 和 `Changed` 集合,并分别调用 `Insert` 和 `Update`。我们还使用了 `TransactionScope` 对象,以确保所有这些更改都以原子方式提交。

原子方式意味着要么所有更改都更新到数据库,要么都没有更新。

public void Committ()
{
    using (TransactionScope scope = new TransactionScope())
    {
        foreach (T o in Changed)
        {
            o.Update();
        }
        foreach (T o in New)
        {
            o.Insert();
        }
        scope.Complete();
    }
}

下面是 `SimpleExampleUOW` 的完整代码

public class SimpleExampleUOW
{
    private List<IEntity> Changed = new List<IEntity>();
    private List<IEntity> New = new List<IEntity>();
    public void Add(IEntity obj)
    {
        New.Add(obj);
    }
    public void Committ()
    {
        using (TransactionScope scope = new TransactionScope())
        {
            foreach (IEntity o in Changed)
            {
                o.Update();
            }
            foreach (IEntity o in New)
            {
                o.Insert();
            }
            scope.Complete();
        }    
    }
   public  void Load(IEntity o)
    {
        Changed  = o.Load() as List<IEntity>;
    }
} 

步骤 4:查看其工作情况 

在客户端,我们可以创建 `Customer` 对象,在内存中添加业务对象,最后通过调用 `Commit` 方法将所有这些更改以原子方式发送到物理数据库。

Customer Customerobj = new Customer();// record 1 Customer
Customerobj.Id = 1000;
Customerobj.CustomerName = "shiv";

Supplier SupplierObj = new Supplier(); // Record 2 Supplier
Supplierobj.Id = 2000;
Supplierobj.SupplierName = "xxxx";

SimpleExampleUOW UowObj = new SimpleExampleUOW();
UowObj.Add(Customerobj); // record 1 added to inmemory
UowObj.Add(Supplierobj); // record 1 added to inmemory
UowObj.Committ(); // The full inmemory collection is sent for final committ 

工作单元设计模式的源代码

您可以下载上述文章中讨论的源代码

您也可以在此处观看我的工厂设计模式视频

 

如需进一步阅读,请观看以下面试准备视频和分步视频系列。

© . All rights reserved.