Unit of Work & Generic repozytorium with Entity Framework 5

Używam ASP.NET MVC 4 z Entity Framework 5. Mam klasy modeli i mapy encji do mapowania istniejących tabel do tych klas modeli. Wszystko to jest dobrze ustawione i działa świetnie.

Teraz chcę z tego drwić. Stworzyłem jednostkę pracy, która pobiera DataContext i używa generycznego repozytorium. Na tej podstawie zbudowałem usługi, aby móc pobierać dane z wielu repozytoriów na raz i mieć tylko jedną instancję DataContext. To również działa świetnie.

Teraz do problem: chcę przetestować usługi, z danymi makiety. Kiedy tworzę instancję Unit of Work, chcę być w stanie wstawić DataContext, który jest wyśmiewany zamiast rzeczywistego DataContext.

Próbowałem stworzyć interfejs IContext i pozwolić prawdziwemu i wyśmiewanemu DataContext zaimplementować to, ale napotkałem problemy z DbSet. Próbowałem użyć IDbSet i stworzyć FakeDbSet, ale bez powodzenia. Czytałem też w Internecie, że wyśmiewanie kontekstu z IDbSet i używanie FakeDbSet jest złe podejdźcie.

Czy masz pojęcie, jaki byłby najlepszy sposób, aby to osiągnąć? To, co mam teraz, to zachowanie, które chciałbym zachować, ale naprawdę chciałbym móc wyśmiewać dane z klas modelu w DataContext.

zdaję sobie sprawę z tego, że struktura encji zawiera już jednostkę zachowania w pracy i że nie musisz dodawać dodatkowych zachowań do tego. Ale chciałem zawinąć to wewnątrz innej klasy, która śledzi wszystkie repozytoria (o nazwie UnitOfWork klasy).

Edit: napisałem dwa artykuły wyjaśniające moje rozwiązanie zarówno z LINQ jak i Entity Framework.

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

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

Oto mój kod:

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);
}

Iunit of work.cs

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

Repozytorium.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;
        }
    }
}

Przykładowy serwis.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();
    }
}

Przykładowy Kontroler.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();
}
Author: Gaui, 2013-05-23

2 answers

Twoja klasa ExampleService oczekuje IUnitOfWork, co oznacza, że potrzebujesz innego IUnitOfWork, który jest Mockiem, a jego metoda GetRepository() zwróci IRepository Mock.

Na przykład (nie tak naprawdę Mock, ale stub w pamięci):
  public InMemoryRepository<T> : IRepository<T> where T : class
  {
        ........
  }

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

Potem:

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();
}
 10
Author: haim770,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-24 07:43:48

Możesz skorzystać z poniższego linku, który jest bardzo pomocny.

Generic repozytorium wzorzec w aplikacji MVC3 z Entity Framework

Entity Framework and Data Patterns

 0
Author: Md. Nazrul Islam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-28 01:05:15