2013-05-23 9 views
27

Sto utilizzando ASP.NET MVC 4 con Entity Framework 5. Ho classi di modelli e mappe di entità per associare tabelle esistenti a tali classi di modelli. Tutto questo è impostato bene e funziona alla grande.Unità di lavoro e repository generico con Entity Framework 5

Ora voglio prendere in giro questo. Ho creato Unit Of Work che prende DataContext e utilizza un repository generico. Da quel momento ho creato servizi per poter ottenere dati da più repository contemporaneamente e ho solo bisogno di avere un'istanza di DataContext. Anche questo funziona alla grande.

Ora al problema: Voglio testare i servizi, con dati falsi. Quando creo l'istanza Unit Of Work, voglio essere in grado di inserire un DataContext che viene deriso al posto del DataContext reale.

Ho cercato di creare un'interfaccia IContext e lasciare che il DataContext reale e deriso lo implementasse, ma ha incontrato problemi con DbSet. Ho provato a usare IDbSet e creare un FakeDbSet ma senza successo. Ho anche letto su internet che prendersi gioco del contesto con IDbSet e usare FakeDbSet è un approccio sbagliato.

Avete qualche idea di quale sarebbe il modo migliore per raggiungere questo obiettivo? Quello che ho ora è il comportamento che vorrei mantenere, ma mi piacerebbe davvero essere in grado di prendere in giro i dati dalle classi Model nel DataContext.

Sono consapevole del fatto che Entity Framework è già dotato del comportamento Unità di lavoro e che non è necessario aggiungere ulteriore comportamento in più. Ma volevo racchiuderlo all'interno di un'altra classe che tenesse traccia di tutti i repository (chiamata classe UnitOfWork).

Modifica: Ho scritto due articoli che spiegano la mia soluzione con LINQ e Entity Framework.

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

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

Ecco il mio codice:

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

EsempioController.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

Inoltre sto avendo lo stesso problema come lei ha ricordato. Tuttavia, puoi pubblicare il tuo codice per IDbContext e la classe concreta che implementa IDbContext? Questo mi aiuterà molto. Grazie in anticipo. – 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 Non sarebbe meglio creare l'istanza 'UnitOfWork' all'interno della classe' ExampleService' al posto del controller? In questo modo il tuo controller non ha bisogno di sapere di cosa 'DbContext' ha bisogno, lo lascia al servizio. – GFoley83

risposta

10

La classe ExampleService si aspetta IUnitOfWork, che significa che solo bisogno di un altro IUnitOfWork che è un Mock e il suo metodo GetRepository() restituirà un IRepository Mock.

Per esempio (non proprio un Mock, ma 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>(); 
     } 
    } 

Poi:

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

Eccellente, funziona come un fascino! Mi piace meglio della creazione di un contesto fittizio con IDbSet e FakeDbSet. Sembra così sbagliato. Grazie molte per questo! – Gaui

+0

Questo sembra un modo pulito e pulito invece di prendere in giro DBSets, grazie per aver condiviso anche io vagando per lo stesso. –