Unit of work + repository + service layer with dependency injection

22,426

Solution 1

If your purpose for using Unit of Work (UoW) was for testability, you took the wrong path. Unit of work does nothing for testability. Its main purposes is to provide atomic transactions to disparate data sources, provide UoW functionality to a data layer that doesn't already provide it, or to wrap an existing UoW in a way that makes it more easily replaceable... something which you've nullified by using the generic repository (this tightly couples it to Entity Framework anyways).

I suggest you get rid of the Unit of Work completely. Entity Framework is already a UoW. Even Microsoft has changed their mind and no longer recommend UoW with EF.

So, if you get rid of UoW, then you can just use repositories to wrap your EF queries. I don't suggest using a generic repository, as this leaks your data layer implementation all over your code (something your UoW was already doing), but rather create Concrete repoTsitories (these can use generic repositories internally if you like, but the generic repository should not leak outside of your repository).

This means your service layer takes the specific concrete repository it needs. For instance, IPortfolioRepository. Then you have a PortfolioRepository class that inherits from IPortfolioRepository which takes your EF DbContext as a parameter that gets injected by your Depndency Injection (DI) framework. If you configure your DI container to instance your EF context on a "PerRequest" basis, then you can pass the same instance to all your repositories. You can have a Commit method on your repository that calls SavesChanges, but it will save changes to all changes, not just to that repository.

As far as Testability goes, you have two choices. You can either mock the concrete repositories, or you can use the built-in mocking capabilities of EF6.

Solution 2

I have been through that hell hole myself and here's what I have done:

  • Ditch the UoW completely. EF's DBContext is a UoW basically. No point in re-inventing the wheel.

    Per MSDN:

    DbContext Class

    Represents a combination of the Unit-Of-Work and Repository patterns and enables you to query a database and group together changes that will then be written back to the store as a unit.

  • Service layer + Repo layer seemed like a good choice. However, repos are always a leaky abstraction and espcially when DBContext's DbSet are the equivalent of repositories.

  • Then when the need for a Windows service arises, things become muddied further with another layer now. Throw async or background processing in the mix, and things quickly start leaking.

If you ask my 2 cents, I would say go with the service layer + EF, one wrapping business logic, the other one wrapping UOW/Repository pattern.

Alternatively, and for Windows Services especially, I'm finding that moving to a command-query based approach works better. Not only it helps testability, it also helps in asynchronous tasks where I don't have to worry about keeping the DBContext alive even after the request has ended (DBContext is now tied with the command handler and stays alive as long as the async command stays alive).

Now if you've recently ended up digesting all those facts about UOW/Repository pattern, then surely, just even reading about Command-Query pattern will make your mind hurt. I have been down that path but trust me, its worth the time to at least look into it and giving it a try.

These posts may help:

If you're brave enough (after digesting thru CQRS), then take a look at MediatR which implements the Mediator pattern (which basically wraps up command-query with notifications) and allows to work via pub-sub. The pub-sub model suits nicely in the Windows Service and services layer.

Share:
22,426
Guillermo Gomez
Author by

Guillermo Gomez

Updated on May 23, 2020

Comments

  • Guillermo Gomez
    Guillermo Gomez almost 4 years

    I am designing a web application and a windows service and want to use the unit of work + repository layer in conjunction with a service layer, and I am having some trouble putting it all together so that the client apps control the transaction of data with the unit of work.

    The unit of work has a collection of all repositories enrolled in the transaction along with commit and rollback operations

    public interface IUnitOfWork : IDisposable
    {
        IRepository<T> Repository<T>() where T : class;
        void Commit();
        void Rollback();
    }
    

    The generic repository has operations that will be performed on the data layer for a particular model (table)

    public interface IRepository<T> where T : class 
    {
        IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, IList<Expression<Func<T, object>>> includedProperties = null, IList<ISortCriteria<T>> sortCriterias = null);
        PaginatedList<T> GetPaged(Expression<Func<T, bool>> filter = null, IList<Expression<Func<T, object>>> includedProperties = null, PagingOptions<T> pagingOptions = null);
        T Find(Expression<Func<T, bool>> filter, IList<Expression<Func<T, object>>> includedProperties = null);
        void Add(T t);
        void Remove(T t);
        void Remove(Expression<Func<T, bool>> filter);
    }
    

    The concrete implementation of the unit of work uses entity framework under the hood (DbContext) to save the changes to the database, and a new instance of the DbContext class is created per unit of work

    public class UnitOfWork : IUnitOfWork
    {
        private IDictionary<Type, object> _repositories;
        private DataContext _dbContext;
        private bool _disposed;
    
        public UnitOfWork()
        {
            _repositories = new Dictionary<Type, object>();
            _dbContext = new DataContext();
            _disposed = false;
        }
    

    The repositories in the unit of work are created upon access if they don't exist in the current unit of work instance. The repository takes the DbContext as a constructor parameter so it can effectively work in the current unit of work

    public class Repository<T> : IRepository<T> where T : class
    {
        private readonly DataContext _dbContext;
        private readonly DbSet<T> _dbSet;
    
        #region Ctor
        public Repository(DataContext dbContext)
        {
            _dbContext = dbContext;
            _dbSet = _dbContext.Set<T>();
        }
        #endregion
    

    I also have a service classes that encapsulate business workflow logic and take their dependencies in the constructor.

    public class PortfolioRequestService : IPortfolioRequestService
    {
        private IUnitOfWork _unitOfWork;
        private IPortfolioRequestFileParser _fileParser;
        private IConfigurationService _configurationService;
        private IDocumentStorageService _documentStorageService;
    
        #region Private Constants
        private const string PORTFOLIO_REQUEST_VALID_FILE_TYPES = "PortfolioRequestValidFileTypes";
        #endregion
    
        #region Ctors
        public PortfolioRequestService(IUnitOfWork unitOfWork, IPortfolioRequestFileParser fileParser, IConfigurationService configurationService, IDocumentStorageService documentStorageService)
        {
            if (unitOfWork == null)
            {
                throw new ArgumentNullException("unitOfWork");
            }
    
            if (fileParser == null)
            {
                throw new ArgumentNullException("fileParser");
            }
    
            if (configurationService == null)
            {
                throw new ArgumentNullException("configurationService");
            }
    
            if (documentStorageService == null)
            {
                throw new ArgumentNullException("configurationService");
            }
    
            _unitOfWork = unitOfWork;
            _fileParser = fileParser;
            _configurationService = configurationService;
            _documentStorageService = documentStorageService;
        }
        #endregion
    

    The web application is an ASP.NET MVC app, the controller gets its dependencies injected in the constructor as well. In this case the unit of work and service class are injected. The action performs an operation exposed by the service, such as creating a record in the repository and saving a file to a file server using a DocumentStorageService, and then the unit of work is committed in the controller action.

    public class PortfolioRequestCollectionController : BaseController
    {
        IUnitOfWork _unitOfWork;
        IPortfolioRequestService _portfolioRequestService;
        IUserService _userService;
    
        #region Ctors
        public PortfolioRequestCollectionController(IUnitOfWork unitOfWork, IPortfolioRequestService portfolioRequestService, IUserService userService)
        {
            _unitOfWork = unitOfWork;
            _portfolioRequestService = portfolioRequestService;
            _userService = userService;
        }
        #endregion
    [HttpPost]
        [ValidateAntiForgeryToken]
        [HasPermissionAttribute(PermissionId.ManagePortfolioRequest)]
        public ActionResult Create(CreateViewModel viewModel)
        {
            if (ModelState.IsValid)
            {
                // validate file exists
                if (viewModel.File != null && viewModel.File.ContentLength > 0)
                {
                    // TODO: ggomez - also add to CreatePortfolioRequestCollection method
                    // see if file upload input control can be restricted to excel and csv
                    // add additional info below control
                    if (_portfolioRequestService.ValidatePortfolioRequestFileType(viewModel.File.FileName))
                    {
                        try
                        {
                            // create new PortfolioRequestCollection instance
                            _portfolioRequestService.CreatePortfolioRequestCollection(viewModel.File.FileName, viewModel.File.InputStream, viewModel.ReasonId, PortfolioRequestCollectionSourceId.InternalWebsiteUpload, viewModel.ReviewAllRequestsBeforeRelease, _userService.GetUserName());
                            _unitOfWork.Commit();                            
                        }
                        catch (Exception ex)
                        {
                            ModelState.AddModelError(string.Empty, ex.Message);
                            return View(viewModel);
                        }
    
                        return RedirectToAction("Index", null, null, "The portfolio construction request was successfully submitted!", null);
                    }
                    else
                    {
                        ModelState.AddModelError("File", "Only Excel and CSV formats are allowed");
                    }
                }
                else
                {
                    ModelState.AddModelError("File", "A file with portfolio construction requests is required");
                }
            }
    
    
            IEnumerable<PortfolioRequestCollectionReason> portfolioRequestCollectionReasons = _unitOfWork.Repository<PortfolioRequestCollectionReason>().Get();
            viewModel.Init(portfolioRequestCollectionReasons);
            return View(viewModel);
        }
    

    On the web application I am using Unity DI container to inject the same instance of the unit of work per http request to all callers, so the controller class gets a new instance and then the service class that uses the unit of work gets the same instance as the controller. This way the service adds some records to the repository which is enrolled in a unit of work and can be committed by the client code in the controller.

    One question regarding the code and architecture described above. How can I get rid of the unit of work dependency at the service classes? Ideally I don't want the service class to have an instance of the unit of work because I don't want the service to commit the transaction, I just would like the service to have a reference to the repository it needs to work with, and let the controller (client code) commit the operation when it see fits.

    On to the windows service application, I would like to be able to get a set of records with a single unit of work, say all records in pending status. Then I would like to loop through all those records and query the database to get each one individually and then check the status for each one during each loop because the status might have changed from the time I queried all to the time I want to operate on a single one. The problem I have right now is that my current architecture doesn't allow me to have multiple unit of works for the same instance of the service.

    public class ProcessPortfolioRequestsJob : JobBase
    {
        IPortfolioRequestService _portfolioRequestService;
        public ProcessPortfolioRequestsJob(IPortfolioRequestService portfolioRequestService)
        {
            _portfolioRequestService = portfolioRequestService;
        }
    

    The Job class above takes a service in the constructor as a dependency and again is resolved by Unity. The service instance that gets resolved and injected depends on a unit of work. I would like to perform two get operations on the service class but because I am operating under the same instance of unit of work, I can't achieve that.

    For all of you gurus out there, do you have any suggestions on how I can re-architect my application, unit of work + repository + service classes to achieve the goals above?

    I intended to use the unit of work + repository patterns to enable testability on my service classes, but I am open to other design patterns that will make my code maintainable and testable at the same time while keeping separation of concerns.

    Update 1 Adding the DataContext class that inheris from EF's DbContext where I declared my EF DbSets and configurations.

    public class DataContext : DbContext
    {
        public DataContext()
            : base("name=ArchSample")
        {
            Database.SetInitializer<DataContext>(new MigrateDatabaseToLatestVersion<DataContext, Configuration>());
            base.Configuration.ProxyCreationEnabled = false;
        }
    
        public DbSet<PortfolioRequestCollection> PortfolioRequestCollections { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
            modelBuilder.Configurations.Add(new PortfolioRequestCollectionConfiguration());
    
            base.OnModelCreating(modelBuilder);
        }
    }
    
  • Erik Funkenbusch
    Erik Funkenbusch over 9 years
    Two points. First, Generic Repositories are leaky (DbSet is a generic repository as well), Concrete repositories are not (unless you do something silly like returning IQueryables). Second, CQS and CQRS are two different things. CQRS is generally not something most people need to worry about unless they are doing DDD. CQS is a good pattern to follow.
  • Mrchief
    Mrchief over 9 years
    Totally agree with you @ErikFunkenbusch.
  • user441521
    user441521 over 7 years
    This is an interesting approach. It feels a little strange that all repositories have a commit() function and calling it will update all transactions between all repositories and not just that one, but thanks for the alternative idea with this.
  • Erik Funkenbusch
    Erik Funkenbusch over 7 years
    @user441521 - Yes, this is a bit of a gotcha, the alternative is creating a new instance for each repository, but then you have to be careful to not mix proxied objects between them. A unit of work can be a messy proposition if you're not careful, regardless of how you implement it. Abstracting it gets messy as well.