【发布时间】:2014-01-31 13:10:47
【问题描述】:
我正在开发一个 Web API 项目(ninject、原始 sql 查询、存储库模式、UoW 模式)我几乎在所有地方都检查过一篇文章,该文章描述了使用不使用实体框架的简单数据库事务实现 UoW(普通的 SQL 请求和 SqlConnection 等...),但找不到任何东西。
我遇到的问题如下。我有一个 Web API,它的控制器与存储库一起使用,而存储库又通过通过 UoW 注入到其中的 DBManager 类与 DB 一起使用。
假设我在存储库中有 2 个方法,每个方法都更新数据库中的数据:
方法 1 - 更新工单(添加客户的帖子)。 方法 2 - 更新工单的状态(仅当发布成功时)。
这些方法可以一个接一个地调用,也可以单独调用,例如 Method2 可以在票据关闭后从其他方法调用。
Method1,在更新DB之前通过DBManager创建一个事务。然后它更新 DB 并调用 Method2 来做他的事情。 Method2,因为它也可以作为一个独立的方法调用,所以也在更新数据库之前启动事务。执行查询时,它会提交事务并返回到 Method1。在这个阶段,方法 1 也提交事务,因为没有异常,并且它希望保留它对数据库所做的更改。但是,它不能,因为 Method2 已经提交了更改。
所以动作图类似于下图:
Method1()
DBManager.BeginTransaction() - begins new transaction
update DB - adds post to the ticket
Method2() - calls method 2 to update ticket status
DBManager.BeginTransaction() - returns transaction started by Method1()
update DB - updates ticket status
DBManager.CommitTransaction() - commits transaction
return
DBManager.CommitTransaction() - commits transaction to save ALL changes but can't since transaction was already committed.
如果我需要在工单状态更新后调用其他方法,则该方法将使用全新的数据集,因为更改已通过 Method2() 提交到数据库中。
我开始考虑如何解决这个问题,但找不到任何东西。我已阅读有关 TransactionScope 的信息,并认为我可以这样做:
public class UnitOfWork : IUnitOfWork, IDisposable
{
/// <summary>
/// DB context.
/// </summary>
private IDBManager _dbManager;
/// <summary>
/// Repository provider class which can create repositories on demand.
/// </summary>
private IRepositoryProvider _repositoryProvider;
private TransactionScope _transaction;
public UnitOfWork(IDBManager dbManager, IRepositoryProvider repoProvider)
{
_dbManager = dbManager;
_repositoryProvider = repoProvider;
}
public T GetRepository<T>()
{
if (_transaction == null)
_transaction = new TransactionScope();
return _repositoryProvider.Create<T>(_dbManager);
}
public void Save()
{
_transaction.Complete();
}
public void Dispose()
{
_transaction.Dispose();
}
}
在这种情况下,TransactionScope 将在我创建我的第一个存储库时开始,然后我可以在我的控制器中调用 save,如下所示:
public TicketPost AddTicketPost(int tid, TicketPost update)
{
TicketPost post = Uow.GetRepository<ITicketsRepository>().AddPost(tid, update);
Uow.Save();
return post;
}
但是,这意味着将为任何操作创建 TransactionScope - 选择/更新/删除,并且它会从创建第一个存储库的那一刻(即使我可能不需要它)持续到事务的那一刻已处置或已完成。
另一种解决方案是使用 DBManager 的事务并从控制器调用 BeginTransaction,并在需要时提交或回滚。像这样:
Uow.BeginTransaction();
try
{
TicketPost post = Uow.GetRepository<ITicketsRepository>().AddPost(tid, update);
}
catch (Exception e)
{
Uow.RollbacTransaction();
}
Uow.CommitTransaction();
但我不太喜欢这种方法。在第一种情况下,我需要捕获异常,这些异常会冒泡到我的 ExceptionsHandler ,这会为客户端创建响应消息。另外,我认为控制器是一个中间人,它收到请求并说“嘿,存储库,这是数据,我已经检查过了,做你的事情,然后给我回电话。”。当它从存储库收到“回电”时,它可能会做一些与数据无关的事情,比如发送电子邮件。我喜欢控制器不需要在同一个存储库中一一调用方法并考虑完成工作需要做的事情,例如:
- 更新工单
- 设置状态
- 用票做别的事
- 发送电子邮件
而不是这个,控制器要求存储库处理票证更新并等待它可以发送电子邮件:
- 告诉控制器他需要做的任何事情来更新票证。
- 等待并发送电子邮件。
我对控制器和存储库的处理方式可能是错误的。如果我错了,请纠正我。
我希望有人可以向我指出一个资源,或者可能有人有类似的设置并且已经找到了针对这种情况的解决方案(交易问题)。
任何帮助将不胜感激。非常感谢您。
【问题讨论】:
标签: c# entity-framework transactions asp.net-web-api transactionscope