HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Unit of work + repository + service layer with dependency injection

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
layerwithdependencyservicerepositoryworkinjectionunit

Problem

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 Repository() 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 where T : class 
{
    IEnumerable Get(Expression> filter = null, IList>> includedProperties = null, IList> sortCriterias = null);
    PaginatedList GetPaged(Expression> filter = null, IList>> includedProperties = null, PagingOptions pagingOptions = null);
    T Find(Expression> filter, IList>> includedProperties = null);
    void Add(T t);
    void Remove(T t);
    void Remove(Expression> 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 _repositories;
    private DataContext _dbContext;
    private bool _disposed;

    public UnitOfWork()
    {
        _repositories = new Dictionary();
        _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 : IRepository where T : class
{
private readonly DataContext _dbContext;
private readonly DbSet _dbSet;

#region Ct

Solution

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


The goal of a unit of work is to abstract the disposable stuff that's implementing it. By making your interface extend IDisposable, you've made a leaky abstraction - now anyone (including your mocking framework) that wants to implement this interface must also implement IDisposable, even though the only implementation that clearly needs it, is the one that's wrapping a DbContext.

I'm not sure I'm following here:

public class UnitOfWork : IUnitOfWork
{
    private IDictionary _repositories;
    private DataContext _dbContext;
    private bool _disposed;

    public UnitOfWork()
    {
        _repositories = new Dictionary();
        _dbContext = new DataContext();
        _disposed = false;
    }


DataContext lives in the System.Data.Linq namespace - that's LINQ-to-SQL, not Entity Framework! I'm not all that familiar with LINQ-to-SQL, and I don't know how much it differs from an EF DbContext - I pretty much skipped L2S and went from ADO.NET to EF...

With EF, I'd consider my DbContext-derived class as my unit of work, and make it implement my IUnitOfWork interface - I'd just tweak yours a little:

public interface IUnitOfWork
{
    IDbSet Repository() where T : class;
    void Commit();
    void Rollback();
}


And then your DataContext class (bad, way too generic name IMO) needs only to expose IDbSet - which is a repository.

Yes! Entity Framework implements Unit-of-Work + Repository patterns for you!

EF is an abstraction all by itself; wrapping it in yet another abstraction is overkill and introduces useless complexity; there's no need for a UnitOfWork class, and no need for a Repository class.

Don't get me wrong - I love UoW+Repository. It's the best thing since sliced bread, I've implemented it over ADO.NET, and even over ADODB in VB6/VBA. But over Entity Framework, it's overkill.

#region is not needed, I'd remove all of them. It's good that you're not using it inside methods (that would mean your method is doing too many things), but this isn't useful in any way:

#region Private Constants
private const string PORTFOLIO_REQUEST_VALID_FILE_TYPES = "PortfolioRequestValidFileTypes";
#endregion


If you change your mind and make it a private static readonly string instead, then you have a comment/region to maintain as well. That said, I would make it a private static readonly string and move its value to config.

Code Snippets

public interface IUnitOfWork : IDisposable
{
    IRepository<T> Repository<T>() where T : class;
    void Commit();
    void Rollback();
}
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;
    }
public interface IUnitOfWork
{
    IDbSet<T> Repository<T>() where T : class;
    void Commit();
    void Rollback();
}
#region Private Constants
private const string PORTFOLIO_REQUEST_VALID_FILE_TYPES = "PortfolioRequestValidFileTypes";
#endregion

Context

StackExchange Code Review Q#63461, answer score: 7

Revisions (0)

No revisions yet.