1
votes

Current System:

I am working on a project which is multi layered as shown below (in order of flow) and I am learning and trying to implement Repo Pattern with UOW on EF Database first.

  • Service (Web API)
  • Business (C# Class Library)
  • Repository (Repo Pattern + UOW)
  • ViewModels (Used by my Service for sent to my UI layer)
  • Data (Entities)
  • Database (SQL Server)

Repository:

Generic Repository:

public interface IRepository<TEntity> where TEntity : class
{
    void Create(TEntity entity);
    IQueryable<TEntity> ReadAll();
    TEntity ReadById();
    IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate);
    TEntity ReadSingle(Expression<Func<TEntity, bool>> predicate);
    TEntity ReadSingleOrDefault(Expression<Func<TEntity, bool>> predicate);
    void Delete(TEntity entity);
}

Repository Implementation:

internal class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
    private bool _isDisposed;

    private readonly DbSet<TEntity> _dbSet;

    private Entities _entities;

    public RepositoryBase(Entities entities)
    {
        this._entities = entities;
        this._dbSet = _entities.Set<TEntity>();
    }

    public IQueryable<TEntity> ReadAll()
    {
        return _dbSet;
    }

    public TEntity ReadById()
    {
        // Dummy return. Don't worry about returning null will be implemented later.
        return null;
    }

    public IQueryable<TEntity> Filter(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Where(predicate);
    }

    public TEntity ReadSingle(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Single(predicate);
    }

    public TEntity ReadSingleOrDefault(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.SingleOrDefault(predicate);
    }

    public void Create(TEntity entity)
    {
        _dbSet.Add(entity);
    }

    public void Delete(TEntity entity)
    {
        _dbSet.Remove(entity);
    }

    public virtual void Dispose(bool isManuallyDisposing)
    {
        if (!_isDisposed)
        {
            if (isManuallyDisposing)
                _tatwaEntities.Dispose();
        }

        _isDisposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~RepositoryBase()
    {
        Dispose(false);
    }

Generic UOW:

public interface IUnitOfWork
{
    IRepository<MyEntity> MyEntityRepository { get; }
    //Other EntityRepositories

    void SaveChanges();
}

UOW Implementation:

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private Entities _entities;

    public UnitOfWork()
    {
        _entities = new entities();
    }

    private IRepository<MyEntity> _myEntityRepository;
    public IRepository<MyEntity> MyEntityRepository
    {
        get
        {
            return _myEntityRepository ?? (_myEntityRepository = new RepositoryBase<MyEntity>(_entities));
        }
    }

    public void SaveChanges()
    {
        _entities.SaveChanges();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_entities != null)
            {
                _entities.Dispose();
                _entities = null;
            }
        }
    }
}

Business Logic

public List<MyViewModel> ReadMyViewModelList()
    {
        var myVMList = new List<MyViewModel>();
        using (var unitOfWork = new UnitOfWork())
        {
            userList.AddRange(unitOfWork.MyEntityRepository.ReadAll().Select(myEntity => new myViewModel
            {
                Prop1 = myEntity.Prop1,
                Prop1 = myEntity.Prop2,
        etc...
            }));
        }

        return myVMList;
    }

Problem:

  • My problem now is that, in my business code, when i use UOW to get data, I have to cast MyEntity to MyViewModel which I think is wrong. because the whole idea of using Repo Pattern with UOW is to abstract and avoid DB/Entity dependency on the business code and be able to independently test that layer.
  • I am creating an instance of my DBEntities in both ReposirotyBase and UOW implemntation classes, which again I think is wrong and should only be created once and I am not sure where is the best place and way yo create that instance.
  • Is my architecture model the correct approach? If not can someone please suggest the appropriate changes to it so as to make it more testable, maintainable and sound.

Hope my question is elaborate. As I am in my learning phase of laying down architecture, I appreciate any additional suggestions/comments/improvements.

Cheers.

1
Repository is an abstraction on storage, EF is an abstraction on Relational Databases. do you really need an abstraction on storage? unneeded layers of abstraction is unneeded complexity is not good Also Generic Repositories are seen by many developers as an anti-pattern, for example ayende.com/blog/3955/repository-is-the-new-singletonDavid DV

1 Answers

3
votes

Here's how I design my repositories, hope it helps. The contract looks like :

public interface IFooRepository
{
  void Add(Foo foo);
  IReadonlyList<Foo> GetAll();
  IReadonlyList<Foo> GetAParticularSubset();
  Foo GetById(int id);
  Foo Remove(int id);
}
  • Notice the collection semantics. The repository is designed as if it were an in-memory list of domain objects you can add to, remove or query from. The primary role of a Repository is to act as an abstraction for us to pretend that the database never existed. So no Update(), no Commit() or such. Methods return simple readonly lists of objects, no IQueryable because it would be a leaky abstraction from your lower persistence layer with unneeded features such as advanced query manipulation.

  • Internally, concrete EF repositories have a DbContext. They use it to add/remove stuff and query, but not to save changes (see below).

  • Unit of Work has a dumb simple signature :

    public interface IUnitOfWork { void SaveChanges(); }

    This is the "I commit my business transaction, please flush it to whatever all this has to be stored into" part that's missing from the Repository. We basically don't want it to be in the repo because Separation of Concerns. Providing people with a bunch of objects and providing a context which keeps track of changes in these objects in relation with a persistent store are two very different things.

    The tricky thing is, as it turns out, Entity Framework can and will do both under the covers for you. Both features are embodied in a DbContext. But while Repository will call DbContext.Foos.Add/Remove/query methods, UnitOfWork will only call DbContext.SaveChanges().

  • Consequently, both Repository and UnitOfWork will need to have a reference to a DbContext. It has to be the same instance of DbContext if you want changes in the objects to take effect. This can be done by injecting the same instance into both via your DI Container (assuming you're using one).

  • From a consumer standpoint, you only need to place yourself in a UnitOfWork scope when you intend to change the objects. For simple querying where the results won't be modified, just use a Repo.