工作单元设计模式






4.84/5 (103投票s)
本文将讨论工作单元设计模式。
目录
- 您是设计模式新手吗?
- 工作单元设计模式有什么用?
- 软件应用程序中的“工作”和“单元”是什么?
- 逻辑事务!= 物理 CRUD
- 所以这可以通过使用简单的事务来实现吗?
- 那么我们如何实现这一点呢?
- 工作单元的 C# 示例代码
- 步骤 1:为业务对象创建通用接口 (IEntity)
- 步骤 2:实现“IEntity”接口
- 步骤 3:创建工作单元集合
- 步骤 4:查看其工作情况
- 工作单元设计模式的源代码
您是设计模式新手吗?
如果您是设计模式的完全新手,可以从下面的 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` 泛型集合将包含新的业务实体/记录。
请注意,在此演示中,我没有实现删除功能。我把它留给您作为家庭作业 。
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
工作单元设计模式的源代码
您可以下载上述文章中讨论的源代码。
您也可以在此处观看我的工厂设计模式视频
如需进一步阅读,请观看以下面试准备视频和分步视频系列。