2013-05-23 13 views
27

Używam ASP.NET MVC 4 z Entity Framework 5. Mam klasy modeli i mapy obiektów, aby mapować istniejące tabele do tych klas modeli. Wszystko to jest dobrze ustawione i działa świetnie.Jednostka pracy i repozytorium generycznego z Entity Framework 5

Teraz chcę kpić z tego. Stworzyłem Unit Of Work, który pobiera DataContext i używa Generic Repository. Po tym zbudowałem usługi, aby móc pobierać dane z wielu repozytoriów jednocześnie i tylko potrzebować jednej instancji DataContext. To również działa świetnie.

Teraz na problem: Chcę przetestować usługi, z próbnymi danymi. Kiedy tworzę instancję Unit Of Work, chcę móc wstawić DataContext, który jest wyśmiewany zamiast prawdziwego DataContext.

Próbowałem utworzyć interfejs IContext i pozwolić realnej i wyszydzanej implementacji DataContext, ale wystąpiły problemy z DbSet. Próbowałem użyć IDbSet i utworzyć FakeDbSet, ale bez powodzenia. Czytam również w Internecie, że kpiny z kontekstu z IDbSet i używanie FakeDbSet jest złym podejściem.

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

Mam świadomość, że Entity Framework ma już funkcję Unit of Work i że nie ma potrzeby dodatkowego zachowania. Ale chciałem zawinąć to wewnątrz innej klasy, która śledzi wszystkie repozytoria (zwane klasą UnitOfWork).

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

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

Ja również mający ten sam problem jak wspomniano. Czy możesz jednak opublikować swój kod dla IDbContext i konkretnej klasy implementującej IDbContext? To mi bardzo pomoże. Z góry dziękuję. – NoobDeveloper

+0

@Nexus [http://gaui.is/how-to-mock-the-datacontext-entity-framework/](http://gaui.is/how-to-mock-the-datacontext-entity-framework/) – Gaui

+1

@ Gaui Czy nie byłoby lepiej utworzyć instancję 'UnitOfWork' wewnątrz klasy' ExampleService' zamiast kontrolera? W ten sposób twój kontroler nie musi wiedzieć o tym, czego potrzebuje 'DbContext', pozostawia to tej usłudze. – GFoley83

Odpowiedz

10

Twoja klasa ExampleService spodziewa IUnitOfWork, co oznacza, że ​​po prostu trzeba innego IUnitOfWork że jest fałszywa i jego metody GetRepository() zwróci IRepository Mock.

Na przykład (nie naprawdę Mock, ale w pamięci en):

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

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

Następnie:

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(); 
} 
+0

Doskonały, działa jak urok! Podoba mi się to lepiej niż tworzenie fałszywego kontekstu z IDbSet i FakeDbSet. To wydaje się takie złe. Bardzo ci za to dziękuję! – Gaui

+0

To wydaje się czysty i czysty sposób, zamiast kpić z DBSetów, dziękuję za udostępnienie, również błąkałem się po to samo. –

Powiązane problemy