Combining Unit of Work
and Repository Pattern
is something used fairly widely nowadays. As Martin Fowler says a purpose of using UoW
is to form a Business Transaction while being ignorant of how repositories actually work (being persistent ignorant). I've reviewed many implementations; and ignoring specific details (concrete/abstract class, interface,...) they are more or less similar to what follows:
public class RepositoryBase<T> { private UoW _uow; public RepositoryBase(UoW uow) // injecting UoW instance via constructor { _uow = uow; } public void Add(T entity) { // Add logic here } // +other CRUD methods } public class UoW { // Holding one repository per domain entity public RepositoryBase<Order> OrderRep { get; set; } public RepositoryBase<Customer> CustomerRep { get; set; } // +other repositories public void Commit() { // Psedudo code: For all the contained repositories do: store repository changes. } }
Now my problem:
UoW
exposes public method Commit
to store the changes. Also, because each repository has a shared instance of UoW
, each Repository
can access method Commit
on UoW. Calling it by one repository makes all other repositories store their changes too; hence the result the whole concept of transaction collapses:
class Repository<T> : RepositoryBase<T> { private UoW _uow; public void SomeMethod() { // some processing or data manipulations here _uow.Commit(); // makes other repositories also save their changes } }
I think this must be not allowed. Considering the purpose of the UoW
(business transaction), the method Commit
should be exposed only to the one who started a Business Transaction for example Business Layer. What surprised me is that I couldn't find any article addressing this issue. In all of them Commit
can be called by any repo being injected.
PS: I know I can tell my developers not to call Commit
in a Repository
but a trusted Architecture is more reliable than trusted developers!
The Repository pattern. Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.
By definition, the Repository Design Pattern in C# mediates between the domain and the data mapping layers using a collection-like interface for accessing the domain objects. Repository Design Pattern separates the data access logic and maps it to the entities in the business logic.
Unit of Work is the concept related to the effective implementation of the repository pattern. non-generic repository pattern, generic repository pattern. Unit of Work is referred to as a single transaction that involves multiple operations of insert/update/delete and so on.
Unit of Work merges many small database updates in a single batch to optimize the number of round-trips. MartinFowler.com says. Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. Programmatic Example.
I do agree with your concerns. I prefer to have an ambient unit of work, where the outermost function opening a unit of work is the one that decides whether to commit or abort. Functions called can open a unit of work scope which automatically enlists in the ambient UoW if there is one, or creates a new one if there is none.
The implementation of the UnitOfWorkScope
that I used is heavily inspired by how TransactionScope
works. Using an ambient/scoped approach also removes the need for dependency injection.
A method that performs a query looks like this:
public static Entities.Car GetCar(int id) { using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Reading)) { return uow.DbContext.Cars.Single(c => c.CarId == id); } }
A method that writes looks like this:
using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Writing)) { Car c = SharedQueries.GetCar(carId); c.Color = "White"; uow.SaveChanges(); }
Note that the uow.SaveChanges()
call will only do an actual save to the database if this is the root (otermost) scope. Otherwise it is interpreted as an "okay vote" that the root scope will be allowed to save the changes.
The entire implementation of the UnitOfWorkScope
is available at: http://coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/
Make your repositories members of your UoW. Don't let your repositories 'see' your UoW. Let UoW handle the transaction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With