C# 存储库和工作单元模式-如何保存更改
我一直在努力理解存储库和工作单元模式之间的关系,尽管这种问题被问了很多次。从本质上讲,我仍然不知道哪个部分将保存/提交数据更改—存储库还是工作单元 因为我看到的每个示例都与将这些与数据库/或映射器结合使用有关,所以让我们制作一个更有趣的示例-让我们将数据持久化到数据文件中的文件系统;根据模式,我应该能够做到这一点,因为数据的去向是不相关的 因此,对于一个基本实体:C# 存储库和工作单元模式-如何保存更改,c#,repository-pattern,unit-of-work,C#,Repository Pattern,Unit Of Work,我一直在努力理解存储库和工作单元模式之间的关系,尽管这种问题被问了很多次。从本质上讲,我仍然不知道哪个部分将保存/提交数据更改—存储库还是工作单元 因为我看到的每个示例都与将这些与数据库/或映射器结合使用有关,所以让我们制作一个更有趣的示例-让我们将数据持久化到数据文件中的文件系统;根据模式,我应该能够做到这一点,因为数据的去向是不相关的 因此,对于一个基本实体: public class Account { public int Id { get; set; } public
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
}
我设想将使用以下接口:
public interface IAccountRepository
{
Account Get(int id);
void Add(Account account);
void Update(Account account);
void Remove(Account account);
}
public interface IUnitOfWork
{
void Save();
}
我认为从使用角度来看,它应该是这样的:
IUnitOfWork unitOfWork = // Create concrete implementation here
IAccountRepository repository = // Create concrete implementation here
// Add a new account
Account account = new Account() { Name = "Test" };
repository.Add(account);
// Commit changes
unitOfWork.Save();
请记住,所有数据都将持久化到文件中,实际添加/更新/删除这些数据的逻辑在哪里
Add()
、Update()
和Remove()
方法进入存储库?在我看来,将所有读/写文件的代码放在一个地方是合乎逻辑的,但是IUnitOfWork
接口的意义是什么IUnitOfWork
实现中,在这种情况下,哪个还将负责数据更改跟踪?对我来说,这意味着存储库可以读取文件,而工作单元必须写入文件,但逻辑现在分为两个位置事实上,我对这一点还很陌生,但没有比我更聪明的人发表过: 正如您所期望的,CRUDs发生在存储库中的代码,但是当调用Account.Add(例如)时,所发生的只是将Account对象添加到稍后要添加的内容列表中(跟踪更改) 调用unitOfWork.Save()时,允许存储库查看其已更改内容的列表或UoW已更改内容的列表(取决于您选择如何实现该模式)并采取适当的行动-因此,在您的情况下,可能会有一个
List NewItemsToAdd
字段,该字段根据对.add()的调用跟踪要添加的内容。当UoW表示可以保存时,存储库实际上可以将新项目作为文件持久化,如果成功,则清除要添加的新项目列表
好的,UoW的重点是跨多个存储库管理保存(这些存储库是我们要提交的逻辑工作单元)
我很喜欢你的问题。
我在EntityFramework中使用了Uow/Repository模式,它显示了EF的实际作用(在最终调用SaveChanges之前,上下文如何跟踪更改)。要在示例中实现此设计模式,您需要编写大量代码来管理更改。存储库可以在没有工作单元的情况下工作,因此也可以使用Save方法
public interface IRepository<T>
{
T Get(int id);
void Add(T entity);
void Update(T entity);
void Remove(T entity);
void Save();
}
公共接口IRepository
{
T Get(int-id);
无效添加(T实体);
无效更新(T实体);
无效删除(T实体);
作废保存();
}
当您有多个存储库(可能有不同的数据上下文)时,使用工作单元。它跟踪事务中的所有更改,直到调用Commit方法将所有更改持久化到数据库(在本例中为文件)
因此,当您在存储库中调用Add/Update/Remove时,它只会更改实体的状态,将其标记为Added、Removed或Dirty。。。调用Commit时,工作单元将在存储库中循环并执行实际的持久性:
- 如果存储库共享相同的数据上下文,则工作单元可以直接与数据上下文一起工作以获得更高的性能(在本例中是打开并写入文件)
- 如果存储库具有不同的数据上下文(不同的数据库或文件),工作单元将在同一TransactionScope中调用每个存储库的Save方法
呃,事情很棘手。想象一下这个场景:一个repo将一些东西保存在数据库中,另一个保存在文件系统中,第三个保存在云上。你是怎么做到的 作为指导原则,UoW应该提交一些东西,但是在上面的场景中,提交只是一个幻觉,因为您有3个非常不同的东西要更新。输入最终一致性,这意味着所有事情最终都是一致的(与使用RDBMS的时间不同) 在消息驱动的体系结构中,UoW被称为传奇。关键是每个saga位可以在不同的时间执行。只有当所有3个存储库都更新时,Saga才会完成 您不会经常看到这种方法,因为大多数时候您将使用RDBMS,但现在NoSql非常常见,因此经典的事务性方法非常有限 所以,如果您确定只使用一个rdbms,请使用UoW事务并将关联的连接传递给每个存储库。最后,UoW将调用commit
如果您知道或预期您可能需要使用多个rdbms或不支持事务的存储,请尝试熟悉消息驱动体系结构和saga概念。如果您想自己使用文件系统,那么使用文件系统可能会使事情变得非常复杂 仅在提交UoW时写入。 您需要做的是让存储库将所有IO操作排队到UnitOfWork中。比如:
public class UserFileRepository : IUserRepository
{
public UserFileRepository(IUnitOfWork unitOfWork)
{
_enquableUow = unitOfWork as IEnquableUnitOfWork;
if (_enquableUow == null) throw new NotSupportedException("This repository only works with IEnquableUnitOfWork implementations.");
}
public void Add(User user)
{
_uow.Append(() => AppendToFile(user));
}
public void Uppate(User user)
{
_uow.Append(() => ReplaceInFile(user));
}
}
通过这样做,您可以同时将所有更改写入文件
使用数据库存储库不需要这样做的原因是事务支持内置在数据库中。因此,您可以告诉DB直接启动一个事务,然后使用它来伪造一个工作单元
交易支持
这将非常复杂,因为您必须能够回滚文件中的更改,并防止不同线程/事务在同时事务期间访问相同的文件。通常,存储库处理所有读取,工作单元处理所有读取