Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit of work + repository + service layer with dependency injection

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);
    }
}
like image 521
Guillermo Gomez Avatar asked Sep 20 '14 22:09

Guillermo Gomez


People also ask

Can we use @repository in service layer?

Yeah, your repository could be used to get lightweight entities from EF4; and your service layer could be used to send these back to a specialised model manager (Model in your scenario).

What is difference between dependency injection and Repository pattern?

They really aren't comparable a repository is something you can inject via dependency injection. The aim of DI is to make your application loosely coupled. Rather than specify a concrete implementation you can specify an interface that defines a contract that an implementation must fulfil.

What is the use of Unit of Work?

The unit of work class serves one purpose: to make sure that when you use multiple repositories, they share a single database context. That way, when a unit of work is complete you can call the SaveChanges method on that instance of the context and be assured that all related changes will be coordinated.

What is Repository layer?

Repository layer is added between the domain and data mapping layers to isolate domain objects from details of the database access code and to minimize scattering and duplication of query code. The Repository pattern is especially useful in systems where number of domain classes is large or heavy querying is utilized.


1 Answers

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.

like image 68
Erik Funkenbusch Avatar answered Sep 17 '22 09:09

Erik Funkenbusch