Use of UnitOfWork across multiple Repositories

Jun 2, 2011 at 5:11 AM

Hello,

I'm new to the use of the Repository pattern, so please bear with me if I'm asking an obvious question.

I'm confused by the Silk implementation of UnitOfWork.

This is my understanding of how unit of work is implemented in Silk: The UnitOfWork implementation is a DbContext (MileageStatsDbContext). The various Repositories get the UnitOfWork instance injected via the constructor i.e. they get handed a DbContext. 

In the various Create/Update/Delete methods of the Repositories, UnitOfWork.SaveChanges() is called at the end. This causes the DbContext to create a transaction and commit the changes to the database. How do you make a UnitOfWork span multiple Repository actions and apply all the changes within a single transaction?

E.g. I don't think this code in BusinessServices.cs runs in a transaction: (from \Silk\MileageStats\MileageStats.Services\BusinessServices.cs)

                if (photo != null)
                {
                    DataLayer.VehiclePhoto dataPhoto = photo.ToDataModelPhoto();
                    this.photoRepository.Create(vehicleToAdd.VehicleId, dataPhoto);
                    vehicleToAdd.PhotoId = vehicle.PhotoId = photo.VehiclePhotoId = dataPhoto.VehiclePhotoId;
                    this.vehicleRepository.Update(vehicleToAdd);
                }

Shouldn't the SaveChanges call be made by the Services code outside a Repository? E.g. to a SaveChanges() method on one of the repositories?

Developer
Jun 6, 2011 at 9:17 PM

Hi stusta,

Sorry for the delay in answering this. The current code does not make use of the Unit of Work pattern. By that I mean, even though we have the interface and classes in place, we are not really using the pattern.

I'm investigating how the code ended up in this state, and I'll post back when I know more.

thanks for the catch!

Christopher

Jun 6, 2011 at 11:31 PM

That's a relief, I'm not going mad! Thanks for clarifying that.

Jun 7, 2011 at 6:22 PM

The unit of work can commit changes across repositories or be specific to a single repository depending of if the IUnitOfWork instance is shared or independent to each repository.  The DbContext we pass is per request so a SaveChanges would affect all repositories during a single request.

You are correct that the services layer would typically call SaveChanges rather than in the repository layer.  Because our repositories are built for the connectionless/stateless web, we decided to commit per repository call.  We were also concerned that it would be too easy in the service layer to forget to call SaveChanges and for sake of keeping the reference implementation simple, we didn't want to introduce an IDisposable based transaction wrapper (similar to how System.Transactions works). 

Thanks,

Geoff

Jun 7, 2011 at 8:49 PM

Hi Geof

In the real world it is necessary can commit changes across repositories. Lack of memory of the programmer should not influence the design.

 

Jun 8, 2011 at 12:32 PM

Thanks for the explanation Geoff. Good point about the per-request lifetime of the DbContext, I'm still getting my head around that one.

Given that the Unit of Work can't be used as a kind of transaction wrapper, I suppose the only way to run the code that I originally quoted in a transaction is to wrap it in a TransactionScope. I know that this isn't supported by SQL Compact Edition 4, but is that what you would do if you were running against a normal instance of SQL 2008?