Unidad De Trabajo y Repositorio Genérico con Entity Framework 5


Estoy usando ASP.NET MVC 4 con Entity Framework 5. Tengo clases de modelo y Mapas de entidad para asignar tablas existentes a esas clases de modelo. Todo esto está bien configurado y funciona muy bien.

Ahora quiero burlarme de esto. He creado La Unidad De Trabajo que toma el DataContext y utiliza un Repositorio Genérico. Sobre eso construí servicios para poder obtener datos de muchos repositorios a la vez y solo necesitando tener una instancia del DataContext. Esto también funciona muy bien.

Ahora a la problema: Quiero probar los servicios, con datos simulados. Cuando creo la Unidad De instancia de Trabajo, quiero ser capaz de insertar un DataContext que se burla en lugar del DataContext real.

Intenté crear una interfaz IContext y dejar que el DataContext real y burlado implementara eso, pero me encontré con problemas con DbSet. Intenté usar IDbSet y crear un FakeDbSet pero sin éxito. También leí en Internet que burlarse del contexto con IDbSet y usar un FakeDbSet es un mal enfoque.

¿Tiene alguna idea de cuál sería la mejor manera de lograr esto? Lo que tengo ahora es el comportamiento que me gustaría mantener, pero realmente me gustaría poder simular los datos de las clases Modelo en el DataContext.

Soy consciente de que Entity Framework ya viene con Unit Of Work behavior y que no es necesario agregar un comportamiento adicional además de eso. Pero quería envolver eso dentro de otra clase que realiza un seguimiento de todos los repositorios (llamado UnitOfWork clase).

Edit: Escribí dos artículos explicando mi solución con LINQ y Entity Framework.

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

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

Aquí está mi código:

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

Unidad de trabajo.cs

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

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

Unidad de trabajo.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();
}
Author: Gaui, 2013-05-23

2 answers

Su clase ExampleService está esperando IUnitOfWork, eso significa que solo necesita otro IUnitOfWork que sea un Simulacro y su método GetRepository() devolverá un simulacro IRepository.

Por ejemplo (no es realmente un trozo simulado pero En Memoria):

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

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

Entonces:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-24 07:43:48
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-28 01:05:15