patterncsharpMajor
Entity Framework Generic Repository Pattern
Viewed 0 times
genericrepositoryframeworkpatternentity
Problem
Thought 1
with the above approach I will go like Repository and then have a Repository
with the implementation of methods written above.
I think this frees my tests from DbContext dependency.
Thought 2
```
public class RepositoryBase : IDisposable where TContext : DbContext, new()
{
private TContext _DataContext;
protected virtual TContext DataContext
{
get
{
if (_DataContext == null)
{
_DataContext = new TContext();
}
return _DataContext;
}
}
public virtual IQueryable GetAll() where T : class
{
using (DataContext)
{
return DataContext.Set();
}
}
public virtual T FindSingleBy(Expression> predicate) where T : class
{
if (predicate != null)
{
using (DataContext)
{
return DataContext.Set().Where(predicate).SingleOrDefault();
}
}
else
{
throw new ArgumentNullException("Predicate value must be passed to FindSingleBy.");
}
}
public virtual IQueryable FindAllBy(Expression> predicate) where T : class
{
if (pre
public interface IRepository : IDisposable where T : class
{
IQueryable Fetch();
IEnumerable GetAll();
IEnumerable Find(Func predicate);
T Single(Func predicate);
T First(Func predicate);
void Add(T entity);
void Delete(T entity);
void SaveChanges();
}with the above approach I will go like Repository and then have a Repository
with the implementation of methods written above.
I think this frees my tests from DbContext dependency.
Thought 2
```
public class RepositoryBase : IDisposable where TContext : DbContext, new()
{
private TContext _DataContext;
protected virtual TContext DataContext
{
get
{
if (_DataContext == null)
{
_DataContext = new TContext();
}
return _DataContext;
}
}
public virtual IQueryable GetAll() where T : class
{
using (DataContext)
{
return DataContext.Set();
}
}
public virtual T FindSingleBy(Expression> predicate) where T : class
{
if (predicate != null)
{
using (DataContext)
{
return DataContext.Set().Where(predicate).SingleOrDefault();
}
}
else
{
throw new ArgumentNullException("Predicate value must be passed to FindSingleBy.");
}
}
public virtual IQueryable FindAllBy(Expression> predicate) where T : class
{
if (pre
Solution
You should use
By only using
The interface can be mocked, hence unit-tested without a physical db and also used with other ORMs.
It's generally prefered to keep
By doing that you can share the same
Not to forget they can share transactions. :)
Hence you should not dispose the DbContext in the Repository, the Repository really doesn't need to be disposable at all. But the UnitOfWork / DbContext must be disposed by something.
Also, ditch the
Otherwise it looks good.
Here's a couple of good examples:
http://blog.swink.com.au/index.php/c-sharp/generic-repository-for-entity-framework-4-3-dbcontext-with-code-first/
http://www.martinwilley.com/net/code/data/genericrepository.html
http://www.mattdurrant.com/ef-code-first-with-the-repository-and-unit-of-work-patterns/
And here's how I do it:
Model / Common / Business assembly
No references but BCL and other possible models (interfaces/dtos)
Business tests assembly
Only references Model / Common / Business assembly
Entity Framework implementation assembly
References Model and the Entity Framework / System.Data.Entity assemblies
Integrated tests assembly
References everything
Expression> as predicates on your interface and have the RepositoryBase implement that interface.By only using
Func, you won't get translation to L2E etc, but will have to enumerate the entire DB table before you can evaluate the Func.The interface can be mocked, hence unit-tested without a physical db and also used with other ORMs.
It's generally prefered to keep
SaveChanges and the DbContext in a separate IUnitOfWork implementation. You can pass the UnitOfWork to the repository constructor, which can access an internal property on the UOW exposing the DbContext.By doing that you can share the same
DbContext between repositories, and batch updates to several entity roots of different type. You also keep fewer open connections, which are the most expensive resource you have when it comes to DBs.Not to forget they can share transactions. :)
Hence you should not dispose the DbContext in the Repository, the Repository really doesn't need to be disposable at all. But the UnitOfWork / DbContext must be disposed by something.
Also, ditch the
OrderBy predicate to FindBy. Since you return an IQueryable, and use an Expression for the predicate, you can keep building the Queryable after the call. For instance repo.FindBy(it => it.Something == something).OrderBy(it => it.OrderProperty). It will still be translated to "select [fields] from [table] where [predicate] order by [orderprop]" when enumerated.Otherwise it looks good.
Here's a couple of good examples:
http://blog.swink.com.au/index.php/c-sharp/generic-repository-for-entity-framework-4-3-dbcontext-with-code-first/
http://www.martinwilley.com/net/code/data/genericrepository.html
http://www.mattdurrant.com/ef-code-first-with-the-repository-and-unit-of-work-patterns/
And here's how I do it:
Model / Common / Business assembly
No references but BCL and other possible models (interfaces/dtos)
public interface IUnitOfWork : IDisposable
{
void Commit();
}
public interface IRepository
{
void Add(T item);
void Remove(T item);
IQueryable Query();
}
// entity classesBusiness tests assembly
Only references Model / Common / Business assembly
[TestFixture]
public class BusinessTests
{
private IRepository repo;
private ConcreteService service;
[SetUp]
public void SetUp()
{
repo = MockRepository.GenerateStub>();
service = new ConcreteService(repo);
}
[Test]
public void Service_DoSomething_DoesSomething()
{
var expectedName = "after";
var entity = new Entity { Name = "before" };
var list = new List { entity };
repo.Stub(r => r.Query()).Return(list.AsQueryable());
service.DoStuff();
Assert.AreEqual(expectedName, entity.Name);
}
}Entity Framework implementation assembly
References Model and the Entity Framework / System.Data.Entity assemblies
public class EFUnitOfWork : IUnitOfWork
{
private readonly DbContext context;
public EFUnitOfWork(DbContext context)
{
this.context = context;
}
internal DbSet GetDbSet()
where T : class
{
return context.Set();
}
public void Commit()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public class EFRepository : IRepository
where T : class
{
private readonly DbSet dbSet;
public EFRepository(IUnitOfWork unitOfWork)
{
var efUnitOfWork = unitOfWork as EFUnitOfWork;
if (efUnitOfWork == null) throw new Exception("Must be EFUnitOfWork"); // TODO: Typed exception
dbSet = efUnitOfWork.GetDbSet();
}
public void Add(T item)
{
dbSet.Add(item);
}
public void Remove(T item)
{
dbSet.Remove(item);
}
public IQueryable Query()
{
return dbSet;
}
}Integrated tests assembly
References everything
[TestFixture]
[Category("Integrated")]
public class IntegratedTest
{
private EFUnitOfWork uow;
private EFRepository repo;
[SetUp]
public void SetUp()
{
Database.SetInitializer(new DropCreateDatabaseAlways());
uow = new EFUnitOfWork(new YourContext());
repo = new EFRepository(uow);
}
[TearDown]
public void TearDown()
{
uow.Dispose();
}
[Test]
public void Repository_Add_AddsItem()
{
var expected = new Entity { Name = "Test" };
repo.Add(expected);
uow.Commit();
var actual = repo.Query().FirstOrDefault(e => e.Name == "Test");
Assert.IsNotNull(actual);
}
[Test]
public void Repository_Remove_RemovesItem()
{
var expected = new Entity { Name = "Test" };
repo.Add(expected);
uow.Commit();
repo.Remove(expected);
uow.Commit();
Assert.AreEqual(0, repo.Query().Count());
}
}Code Snippets
public interface IUnitOfWork : IDisposable
{
void Commit();
}
public interface IRepository<T>
{
void Add(T item);
void Remove(T item);
IQueryable<T> Query();
}
// entity classes[TestFixture]
public class BusinessTests
{
private IRepository<Entity> repo;
private ConcreteService service;
[SetUp]
public void SetUp()
{
repo = MockRepository.GenerateStub<IRepository<Entity>>();
service = new ConcreteService(repo);
}
[Test]
public void Service_DoSomething_DoesSomething()
{
var expectedName = "after";
var entity = new Entity { Name = "before" };
var list = new List<Entity> { entity };
repo.Stub(r => r.Query()).Return(list.AsQueryable());
service.DoStuff();
Assert.AreEqual(expectedName, entity.Name);
}
}public class EFUnitOfWork : IUnitOfWork
{
private readonly DbContext context;
public EFUnitOfWork(DbContext context)
{
this.context = context;
}
internal DbSet<T> GetDbSet<T>()
where T : class
{
return context.Set<T>();
}
public void Commit()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public class EFRepository<T> : IRepository<T>
where T : class
{
private readonly DbSet<T> dbSet;
public EFRepository(IUnitOfWork unitOfWork)
{
var efUnitOfWork = unitOfWork as EFUnitOfWork;
if (efUnitOfWork == null) throw new Exception("Must be EFUnitOfWork"); // TODO: Typed exception
dbSet = efUnitOfWork.GetDbSet<T>();
}
public void Add(T item)
{
dbSet.Add(item);
}
public void Remove(T item)
{
dbSet.Remove(item);
}
public IQueryable<T> Query()
{
return dbSet;
}
}[TestFixture]
[Category("Integrated")]
public class IntegratedTest
{
private EFUnitOfWork uow;
private EFRepository<Entity> repo;
[SetUp]
public void SetUp()
{
Database.SetInitializer(new DropCreateDatabaseAlways<YourContext>());
uow = new EFUnitOfWork(new YourContext());
repo = new EFRepository<Entity>(uow);
}
[TearDown]
public void TearDown()
{
uow.Dispose();
}
[Test]
public void Repository_Add_AddsItem()
{
var expected = new Entity { Name = "Test" };
repo.Add(expected);
uow.Commit();
var actual = repo.Query().FirstOrDefault(e => e.Name == "Test");
Assert.IsNotNull(actual);
}
[Test]
public void Repository_Remove_RemovesItem()
{
var expected = new Entity { Name = "Test" };
repo.Add(expected);
uow.Commit();
repo.Remove(expected);
uow.Commit();
Assert.AreEqual(0, repo.Query().Count());
}
}Context
StackExchange Code Review Q#19037, answer score: 26
Revisions (0)
No revisions yet.