C# 存储库和工作单元模式 - 如何保存更改
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14263331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Repository and Unit of Work patterns - How to save changes
提问by Peter Monks
I'm struggling to understand the relationship between the Repository and Unit of Work patterns despite this kind of question being asked so many times. Essentially I still don't understand which part would save/commit data changes - the repository or the unit of work?
尽管此类问题被问了很多次,但我仍然难以理解存储库和工作单元模式之间的关系。基本上我仍然不明白哪个部分会保存/提交数据更改 - 存储库还是工作单元?
Since every example I've seen relates to using these in conjunction with a database/OR mapper let's make a more interesting example - lets persist the data to the file system in data files; according to the patterns I should be able to do this because where the data goes is irrelevant.
由于我看到的每个示例都与将这些与数据库/OR 映射器结合使用有关,因此让我们举一个更有趣的示例——让数据持久化到数据文件中的文件系统;根据模式,我应该能够做到这一点,因为数据的去向无关紧要。
So for a basic entity:
所以对于一个基本实体:
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
}
I imagine the following interfaces would be used:
我想将使用以下接口:
public interface IAccountRepository
{
Account Get(int id);
void Add(Account account);
void Update(Account account);
void Remove(Account account);
}
public interface IUnitOfWork
{
void Save();
}
And I think in terms of usage it would look like this:
我认为就使用而言,它看起来像这样:
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();
Bearing in mind that all data will be persisted to files, where does the logic go to actually add/update/remove this data?
请记住,所有数据都将保存到文件中,实际添加/更新/删除这些数据的逻辑在哪里?
- Does it go in the repository via the
Add()
,Update()
andRemove()
methods? It sounds logical to me to have all the code which reads/writes files in one place, but then what is the point of theIUnitOfWork
interface? - Does it go in the
IUnitOfWork
implementation, which for this scenario would also be responsible for data change tracking too? To me this would suggest that the repository can read files while the unit of work has to write files but that the logic is now split into two places.
- 它是否通过
Add()
,Update()
和Remove()
方法进入存储库?将所有读取/写入文件的代码放在一个地方听起来合乎逻辑,但是IUnitOfWork
界面的意义是什么? - 它是否进入
IUnitOfWork
实现,对于这种情况,它也将负责数据更改跟踪?对我来说,这表明存储库可以读取文件,而工作单元必须写入文件,但逻辑现在分为两个地方。
采纳答案by phnkha
Repository can work without Unit Of Work, so it can also have Save method.
Repository 可以在没有 Unit Of Work 的情况下工作,所以它也可以有 Save 方法。
public interface IRepository<T>
{
T Get(int id);
void Add(T entity);
void Update(T entity);
void Remove(T entity);
void Save();
}
Unit Of Work is used when you have multiple repositories (may have different data context). It keeps track of all changes in a transaction until you call Commit method to persist all changes to database(file in this case).
当您有多个存储库(可能有不同的数据上下文)时使用工作单元。它会跟踪事务中的所有更改,直到您调用 Commit 方法将所有更改持久化到数据库(在本例中为文件)。
So, when you call Add/Update/Removein the Repository, it only changes the status of the entity, mark it as Added, Removed or Dirty... When you call Commit, Unit Of Work will loop through repositories and perform actual persistence:
因此,当您在 Repository 中调用Add/Update/Remove时,它只会更改实体的状态,将其标记为已添加、已删除或已脏……当您调用 Commit 时,工作单元将遍历存储库并执行实际的持久化:
If repositories share the same data context, the Unit Of Work can work directly with the data context for higher performance(open and write file in this case).
If repositories have different data context(different databases or files), the Unit Of Work will call each repository's Save method in a same TransactionScope.
如果存储库共享相同的数据上下文,工作单元可以直接使用数据上下文以获得更高的性能(在这种情况下打开和写入文件)。
如果存储库具有不同的数据上下文(不同的数据库或文件),则工作单元将在相同的 TransactionScope 中调用每个存储库的 Save 方法。
回答by Neil Thompson
I'm actually quite new to this but as nobody wiser has posted:
我实际上对此很陌生,但没有人更明智地发布过:
The code which CRUDs happens in the repositories as you would expect, but when Account.Add (for example) is called, all that happens is that an Account object is added to the list of things to be added later (the change is tracked).
CRUD 的代码如您所料在存储库中发生,但是当 Account.Add(例如)被调用时,所有发生的事情就是将 Account 对象添加到稍后要添加的事物列表中(跟踪更改) .
When unitOfWork.Save() is called the repositories are allowed to look through their list of what has changed Or the UoW's list of what has changed (depending on how you choose to implement the pattern) and act appropriately - so in your case there might be a List<Account> NewItemsToAdd
field that has been tracking what to add based on calls to .Add(). When the UoW says it's OK to save, the repository can actually persist the new items as files, and if successful clear the list of new items to add.
当 unitOfWork.Save() 被调用时,存储库被允许查看他们的已更改内容列表或 UoW 已更改内容列表(取决于您选择实现模式的方式)并采取适当的行动 - 所以在您的情况下可能有是一个List<Account> NewItemsToAdd
字段,它根据对 .Add() 的调用跟踪要添加的内容。当 UoW 说可以保存时,存储库实际上可以将新项目保存为文件,如果成功,则清除要添加的新项目列表。
AFAIK the point of the UoW is to manage the Save across multiple repositories (which combined are the logical unit of work that we want to commit).
AFAIK UoW 的重点是管理跨多个存储库的保存(这些存储库是我们要提交的逻辑工作单元)。
I really like your question. I've used Uow / Repository Pattern with Entity Framework and it shows how much EF actually does (how the context tracks the changes until SaveChanges is finally called). To implement this design pattern in your example you need to write quite a bit of code to manage the changes.
我真的很喜欢你的问题。我已经将 Uow / Repository Pattern 与 Entity Framework 一起使用,它显示了 EF 实际做了多少(在最终调用 SaveChanges 之前上下文如何跟踪更改)。要在您的示例中实现此设计模式,您需要编写大量代码来管理更改。
回答by MikeSW
Ehe, things are tricky. Imagine this scenario: one repo saves something in a db, other on the file system and the third something on the cloud. How do you commit that?
呵呵,事情很棘手。想象一下这种情况:一个 repo 将某些内容保存在 db 中,另一个保存在文件系统中,第三个保存在云中。你怎么犯?
As a guideline, the UoW should commit things, however in the above scenario, Commit is just an illusion as you have 3 very different things to update. Enter eventual consistency, which means that all things will be consistent eventually (not in the same moment as you're used with a RDBMS).
作为指导方针,UoW 应该提交一些东西,但是在上面的场景中,提交只是一种错觉,因为你有 3 个非常不同的东西要更新。输入最终一致性,这意味着所有事物最终都将保持一致(与使用 RDBMS 时不同)。
That UoW is called a Saga in a message driven architecture. The point is every saga bit can be executed at different time. Saga completes only when all 3 repositories are updated.
UoW 在消息驱动架构中被称为 Saga。关键是每个传奇位都可以在不同的时间执行。仅当所有 3 个存储库都更新后,Saga 才会完成。
You don't see this approach as often, because most of the time you'll work with a RDBMS, but nowadays NoSql is quite common so a classic transactional approach is very limited.
您不会经常看到这种方法,因为大多数时候您将使用 RDBMS,但如今 NoSql 非常普遍,因此经典的事务方法非常有限。
So, if you're sure you work ONLY with ONE rdbms, use a transaction with the UoW and pass teh associated connection to each repository. At the end, UoW will call commit.
因此,如果您确定只使用一个 rdbms,请使用 UoW 的事务并将关联的连接传递给每个存储库。最后,UoW 将调用 commit。
If you know or expect you might have to work with more than one rdbms or a storage that doesn't support transactions, try to familiarize yourself with a message driven architecture and with the saga concept.
如果您知道或期望您可能需要使用多个 rdbms 或不支持事务的存储,请尝试熟悉消息驱动架构和 saga 概念。
回答by jgauffin
Using the file system can complicate things quite much if you want to do it on yourself.
如果您想自己动手,使用文件系统会使事情变得非常复杂。
Only write when the UoW is committed.
仅在提交 UoW 时写入。
What you have to do is to let the repositories enqueue all IO operations in the UnitOfWork. Something like:
您需要做的是让存储库将 UnitOfWork 中的所有 IO 操作排入队列。就像是:
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));
}
}
By doing so you can get all changes written to the file(s) at the same time.
通过这样做,您可以同时将所有更改写入文件。
The reason that you don't need to do that with DB repositories is that the transaction support is built into the DB. Hence you can tell the DB to start a transaction directly and then just use it to fake a Unit Of Work.
您不需要对 DB 存储库执行此操作的原因是事务支持已内置到 DB 中。因此,您可以告诉数据库直接启动事务,然后使用它来伪造工作单元。
Transaction support
交易支持
Will be complex as you have to be able to roll back changes in the files and also prevent different threads/transactions from accessing the same files during simultaneous transactions.
将很复杂,因为您必须能够回滚文件中的更改,并防止不同的线程/事务在同时进行的事务中访问相同的文件。
回答by LIU YUE
normally, repositories handle all reads, and unit-of-work handles all writes,but for sure you can handle all reads and writes by only using one of these two (but if only using repository pattern, it will be very tedious to maintain maybe 10 repositories,more worse,maybe result in inconsistent reads and writes be overwritten), advantage of mix using both is ease of tracing status change and ease of handling concurrency and consistent problems. for better understanding,you can refer links: Repository Pattern with Entity Framework 4.1 and Parent/Child Relationshipsand https://softwareengineering.stackexchange.com/questions/263502/unit-of-work-concurrency-how-is-it-handled
通常,存储库处理所有读取,而工作单元处理所有写入,但可以肯定的是,您可以仅使用这两者之一来处理所有读取和写入(但如果仅使用存储库模式,维护起来可能会非常繁琐) 10个repositories,更糟糕的是,可能会导致不一致的读写被覆盖),混合使用两者的优点是易于跟踪状态变化以及易于处理并发和一致问题。为了更好地理解,您可以参考链接:Repository Pattern with Entity Framework 4.1 and Parent/Child Relationships和 https://softwareengineering.stackexchange.com/questions/263502/unit-of-work-concurrency-how-is-it-handled