web-dev-qa-db-fra.com

Unité de travail et référentiel générique avec Entity Framework 5

J'utilise ASP.NET MVC 4 avec Entity Framework 5. J'ai des classes de modèle et des cartes d'entité pour mapper des tables existantes à ces classes de modèle. Tout cela est bien configuré et fonctionne très bien.

Maintenant, je veux me moquer de ça. J'ai créé Unit Of Work qui prend le DataContext et utilise un référentiel générique. J'ai ensuite construit des services pour pouvoir obtenir les données de plusieurs référentiels à la fois et n'avoir besoin que d'une instance du DataContext. Cela fonctionne aussi très bien.

Passons maintenant au problème: Je veux tester les services avec des données fictives. Lorsque je crée l'instance d'Unité de travail, je veux pouvoir insérer un DataContext qui est simulé à la place du vrai DataContext.

J'ai essayé de créer une interface IContext et de laisser le DataContext réel et simulé l'implémenter mais j'ai rencontré des problèmes avec DbSet. J'ai essayé d'utiliser IDbSet et de créer un FakeDbSet mais sans succès. J'ai aussi lu sur Internet que se moquer du contexte avec IDbSet et utiliser un FakeDbSet est une mauvaise approche.

Avez-vous une idée de ce qui serait le meilleur moyen d'y parvenir? Ce que j’ai aujourd’hui, c’est le comportement que j’aimerais conserver, mais je voudrais vraiment pouvoir me moquer des données des classes Model du DataContext.

Je suis conscient du fait qu'Entity Framework est déjà livré avec le comportement de Unit Of Work et que vous n'avez pas besoin d'ajouter de comportement supplémentaire. Par ailleurs, je souhaitais intégrer cela dans une autre classe qui garde en mémoire tous les référentiels (appelés classe UnitOfWork).

Edit: J'ai écrit deux articles expliquant ma solution à la fois avec LINQ et Entity Framework.

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

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

Voici mon 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();
}
27
Gaui

Votre classe ExampleService attend IUnitOfWork, cela signifie que vous avez simplement besoin d'une autre IUnitOfWork qui est une simulation et que sa méthode GetRepository() renvoie une erreur IRepository.

Par exemple (pas vraiment un stub simulé mais en mémoire):

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

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

Ensuite:

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
haim770
0
Md. Nazrul Islam