Unit Of Work & Generic Repository with Entity Fram

2019-01-21 01:26发布

问题:

I'm using ASP.NET MVC 4 with Entity Framework 5. I have model classes and Entity Maps to map existing tables to those model classes. All this is setup fine and works great.

Now I want to mock this. I created Unit Of Work that takes the DataContext and uses a Generic Repository. Upon that I built services to be able to get data from many repositories at once and only needing to have one instance of the DataContext. This also works great.

Now to the problem: I want to test the services, with mock data. When I create the Unit Of Work instance, I want to be able to insert a DataContext that is mocked instead of the real DataContext.

I tried to create a IContext interface and let the real and mocked DataContext implement that but ran into problems with DbSet. I tried to use IDbSet and creating a FakeDbSet but without success. I also read on the internet that mocking the context with IDbSet and using a FakeDbSet is a bad approach.

Do you have any idea what would be the best way to achieve this? What I have now is the behavior I would like to keep, but would really like to be able to mock the data from the Model classes in the DataContext.

I'm aware of that Entity Framework already comes with Unit Of Work behavior and that you don't need to add extra behavior on top of that. But I wanted to wrap that inside of another class that keeps track of all the repositories (called UnitOfWork class).

Edit: I wrote two articles explaining my solution with both LINQ and Entity Framework.

http://gaui.is/how-to-mock-the-datacontext-linq/

http://gaui.is/how-to-mock-the-datacontext-entity-framework/

Here's my code:

IRepository.cs

public interface IRepository<T> where T : class
{
    void Add(T entity);
    void Delete(T entity);
    void Update(T entity);
    T GetById(long Id);
    IEnumerable<T> All();
    IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
}

IUnitOfWork.cs

public interface IUnitOfWork : IDisposable
{
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
    void Save();
}

Repository.cs

public class Repository<T> : IRepository<T> where T : class
{
    private readonly IDbContext _context;
    private readonly IDbSet<T> _dbset;

    public Repository(IDbContext context)
    {
        _context = context;
        _dbset = context.Set<T>();
    }

    public virtual void Add(T entity)
    {
        _dbset.Add(entity);
    }

    public virtual void Delete(T entity)
    {
        var entry = _context.Entry(entity);
        entry.State = System.Data.EntityState.Deleted;
    }

    public virtual void Update(T entity)
    {
        var entry = _context.Entry(entity);
        _dbset.Attach(entity);
        entry.State = System.Data.EntityState.Modified;
    }

    public virtual T GetById(long id)
    {
        return _dbset.Find(id);
    }

    public virtual IEnumerable<T> All()
    {
        return _dbset;
    }

    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return _dbset.Where(predicate);
    }
}

UnitOfWork.cs

public class UnitOfWork<TContext> : IUnitOfWork where TContext : IDbContext, new()
{
    private readonly IDbContext _ctx;
    private Dictionary<Type, object> _repositories;
    private bool _disposed;

    public UnitOfWork()
    {
        _ctx = new TContext();
        _repositories = new Dictionary<Type, object>();
        _disposed = false;
    }

    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        if (_repositories.Keys.Contains(typeof(TEntity)))
            return _repositories[typeof(TEntity)] as IRepository<TEntity>;

        var repository = new Repository<TEntity>(_ctx);
        _repositories.Add(typeof(TEntity), repository);
        return repository;
    }

    public void Save()
    {
        _ctx.SaveChanges();
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                _ctx.Dispose();
            }

            this._disposed = true;
        }
    }
}

ExampleService.cs

public class ExampleService
{
    private IRepository<Example> m_repo;

    public ExampleService(IUnitOfWork uow)
    {
        m_repo = uow.GetRepository<Example>();
    }

    public void Add(Example Example)
    {
        m_repo.Add(Example);
    }

    public IEnumerable<Example> getAll()
    {
        return m_repo.All();
    }
}

ExampleController.cs

public IEnumerable<Example> GetAll()
{
    // Create Unit Of Work object
    IUnitOfWork uow = new UnitOfWork<AppDataContext>();

    // Create Service with Unit Of Work attached to the DataContext
    ExampleService service = new ExampleService(uow);

    return service.getAll();
}

回答1:

Your ExampleService class is expecting IUnitOfWork, that means you just need another IUnitOfWork that is a Mock and its GetRepository() method will return an IRepository Mock.

For example (not really a Mock but In-Memory stub):

  public InMemoryRepository<T> : IRepository<T> where T : class
  {
        ........
  }

  public InMemoryUnitOfWork : IUnitOfWork
  {
       public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
       {
            return new InMemoryRepository<TEntity>();
       }
  }

Then:

public IEnumerable<Example> GetAll()
{
    // Create Unit Of Work object
    IUnitOfWork uow = new InMemoryUnitOfWork();

    // Create Service with Unit Of Work
    ExampleService service = new ExampleService(uow);

    return service.getAll();
}


回答2:

You can follow the following link it is very helpful.

Generic Repository Pattern in MVC3 Application with Entity Framework

Entity Framework and Data Patterns