The query I have is a little more complex than the title lets on. Perhaps this is a foolish question, but I couldn't find any certain answer when searching online. Currently, I'm implementing the Repository/Unit-of-Work pattern in my own flavor and it looks a little like this:
// Note: methods are async for conventions, not because
// they're truly async
public interface IUnitOfWork
{
Task Begin();
// The Task<int> is the numbers of rows affected by this commit
Task<int> Commit();
Task Rollback();
}
The repository can more or less be expressed as such:
public interface IWriteableRepository<T>
where T : class
{
EntityEntry<T> Insert(T item);
// Other CRUD methods removed for brevity; they're
// of similar signatures
}
The idea is that an IUnitOfWork
will hold some TransactionScope
instance internally and handle the respective logic.
I then have two concerns. First, if each IUnitOfWork
and IWriteableRepository<T>
instance is injected with different instances of a DbContext
(I'm using EntityFrameworkCore for the time being), will calling DbContext.BeginTransactionAsync()
produce a transaction scope for both in the following code?
await this.UnitOfWork.Begin();
this.Repository.Insert(someEntity);
var rows = await this.UnitOfWork.Commit();
In other words, does the repository only operate on the transaction created in the call to Begin()
, or will it operate completely independently?
The second concern I have is in relation to implementing the IUnitOfWork
interface. My approach thus far has been roughly
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork(DbContext context)
{
this.Context = context;
}
private DbContext Context { get; set; }
private TransactionScope Transaction { get; set; }
public async Task Begin()
{
if (this.Scope == null)
{
this.Transaction = await this.Context
.Database
.BeginTransactionAsync();
}
}
public async Task<int> Commit()
{
if (this.Scope != null)
{
var rows = await this.Context.SaveChangesAsync(false);
this.Scope.Commit();
this.Context.AcceptAllChanges();
return rows;
}
}
public Task Rollback()
{
if (this.Scope != null)
{
this.Scope.Rollback();
this.Scope.Dispose();
this.Scope = null;
}
return Task.CompletedTask;
}
}
I'm mostly unsure whether the Rollback()
method could be improved. I feel like disposing the object explicitly isn't correct. Is there any other way that I should go about handling getting rid of a TransactionScope
?