I am creating a new MVC solution using the instruction of an udemy video. The instructor uses the repository pattern to interact with the models but he uses an int. I am, on the other hand, using a Guid, which he does not show. I know that while an int is created automatically, I need to explicitly create the Guid as I do here:
[HttpPost]
public ActionResult CreateCarrier(Carrier carrier)
{
carrier.CarrierID = System.Guid.NewGuid();
carriers.Insert(carrier);
carriers.Commit();
return RedirectToAction("CarrierList");
}
However, since I have numerous classes I think this would be a lot of extra code. Shouldn't I create the Guid for the entity id right before I insert it? I just cannot figure out how to do it in the repository. Here is the repository code:
public abstract class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : class
{
internal DataContext context;
internal DbSet<TEntity> dbSet;
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
}
and here is the interface:
namespace WarehouseScheduling.DAL.Contracts
{
public interface IRepositoryBase<TEntity> where TEntity : class
{
void Commit();
void Delete(TEntity entity);
void Delete(object id);
void Dispose();
IQueryable<TEntity> GetAll();
IQueryable<TEntity> GetAll(object filter);
TEntity GetById(object id);
TEntity GetFullObject(object id);
IQueryable<TEntity> GetPaged(int top = 20, int skip = 0, object orderBy = null, object filter = null);
void Insert(TEntity entity);
void Update(TEntity entity);
}
}
YOu can enforce the generic type TEntity to be an interface that allow getting and setting Guid
public interface IHasGuid
{
Guid Id { get; set; }
}
Then you can put restriction on your repository class/interface so that it only works with entities of type IHasGuid
namespace WarehouseScheduling.DAL.Contracts
{
public interface IRepositoryBase<TEntity> where TEntity : IHasGuid
{
void Commit();
void Delete(TEntity entity);
void Delete(object id);
void Dispose();
IQueryable<TEntity> GetAll();
IQueryable<TEntity> GetAll(object filter);
TEntity GetById(object id);
TEntity GetFullObject(object id);
IQueryable<TEntity> GetPaged(int top = 20, int skip = 0, object orderBy = null, object filter = null);
void Insert(TEntity entity);
void Update(TEntity entity);
}
}
and also modify the implementation similarly but add code to automatically add in the Guid
public abstract class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : IHasGuid
{
internal DataContext context;
internal DbSet<TEntity> dbSet;
public virtual void Insert(TEntity entity)
{
entity.Id = new Guid();
dbSet.Add(entity);
}
}
Remember that your entity (Carrier in this case) has to implement IHasGuid
public class Carrier : IHasGuid
{
public Guid Id { get; set; }
// other fields below
}
Related
I wrote an application using repository pattern and unit of work, it is also using entity framework in order to interact with db (mssql in my case).
Right now I have two projects in my application: 1-DAL which stands for data access layer, and 2-BLL which stands for bisnel logic layer.
I have my unitofwork, repository (I have one generic repo for all classes), and basic data access controllers(like GetAll, or GetById, Edit, Delete, and Create) in DAL project. Also, I have DTO mapper, and more "logic" controllers in BLL project.
I need some explanation on writing tests for this, like: 1) Do I have to write tests for data access layer? 2) If I have to test the data access layer, do I have to write tests for only controller, or for repository as well (as they are pretty much the same)? 3) Do I have to seed the database in the test cases themselves, or use the "pre seeded db"? 4) Do I have to write tests for mapper (I am using Mapper extension)
p.s.: If there is an article for this theme, I'll be grateful.
I will attach some of my code bellow:
Generic repository:
public class GenericRepository<TEntity> where TEntity : class
{
internal WarehouseContext context;
internal DbSet<TEntity> dbset;
public GenericRepository(WarehouseContext context)
{
this.context = context;
this.dbset = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> GetAll()
{
return dbset;
}
public virtual TEntity GetById(Guid id)
{
return dbset.Find(id);
}
public virtual void Add(TEntity entity)
{
dbset.Add(entity);
}
public virtual void Delete(Guid id)
{
TEntity entityToDelete = dbset.Find(id);
dbset.Remove(entityToDelete);
}
public virtual void Edit(TEntity entityToUpdate)
{
dbset.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
DAL controller for CategoryClass:
private readonly UnitOfWork _unitOfWork;
public CategoryController()
{
_unitOfWork = new UnitOfWork();
}
public List<Category> GetCategories()
{
return _unitOfWork.CategoryRepository.GetAll().ToList();
}
public Category GetCategoryById(Guid id)
{
return _unitOfWork.CategoryRepository.GetById(id);
}
public void AddCategory(Category category)
{
_unitOfWork.CategoryRepository.Add(category);
_unitOfWork.Save();
}
public void EditCategory(Category category)
{
_unitOfWork.CategoryRepository.Edit(category);
_unitOfWork.Save();
}
public void DeleteCategory(Guid id)
{
_unitOfWork.CategoryRepository.Delete(id);
_unitOfWork.Save();
}
Example of what I have in BLL controller for CategoryClass:
private readonly CategoryController _categoryController;
private readonly IMapper _mapper;
public CategoryLogicContoller()
{
_categoryController = new CategoryController();
var profile = new CategoryProfile();
_mapper = profile.CategoryMapper;
}
public List<CategoryDTO_short> GetAllCategories_shortDescription()
{
var categories = _categoryController.GetCategories().ToList();
var categoriesDTO = new List<CategoryDTO_short>();
foreach (var category in categories)
{
var categoryDTO = _mapper.Map<Category, CategoryDTO_short>(category);
categoriesDTO.Add(categoryDTO);
}
return categoriesDTO;
}
I am also attaching the screenshot of my app architecture:
Thanks for answers!
What a coincidence, I implemented unit testing for this pattern a few days ago.
The UnitOfWork should use a generic repository, so you can define a repository for Entity Framework, and a repository that stores data in a list, e.g. MemoryRepository. This way the unit testing is not depending on the database, or actually, also not depending on Entity Framework at all.
Since the code is quite long, I have added a few excerpts and removed a lot of code.
Unit of work with a repository factory
The unit of work receives an instance of DB context and a factory that creates instances of repositories, with the given instance of DB context.
The unit of work will receive a factory instead of a repository. The idea is to give it a different factory when unit testing.
public class UnitOfWork
{
IDbContext _db { get; }
IRepositoryFactory _repositoryFactory { get; }
IRepository<Category> _categoryRepository { get; set; } = null!;
public UnitOfWork(IDbContext db, IRepositoryFactory repositoryFactory)
{
_db = db;
_repositoryFactory = repositoryFactory;
}
public IRepository<Question> Questions
{
get => (_categoryRepository ??= _repositoryFactory.CreateInstance<Category>(_db));
}
public void Commit()
{
_db.Commit();
}
public async Task CommitAsync()
{
await _db.CommitAsync();
}
}
Repository factory pattern
The interface uses an IKey interface, which says that the implementation should have a Guid Id { get; set; } defined on it. I have added this to all the entities, so the memory repository has an index for the dictionary.
IRepositoryInterface
public interface IRepositoryFactory
{
public IRepository<TEntity> CreateInstance<TEntity>(IDbContext db) where TEntity : class, IKey;
}
EFRepositoryFactory
This factory will create an instance of the EF repository, which is used in the application, and added to the DI container in the startup of the application.
public class EFRepositoryFactory : IRepositoryFactory
{
public IRepository<TEntity> CreateInstance<TEntity>(IDbContext db) where TEntity : class, IKey
{
return new EFRepository<TEntity>((FetchDbContext)db);
}
}
MemoryRepositoryFactory
This factory will create an instance of the memory repository, that is used in unit testing.
public class MemoryRepositoryFactory : IRepositoryFactory
{
public IRepository<TEntity> CreateInstance<TEntity>(IDbContext db) where TEntity : class, IKey
{
return new MemoryRepository<TEntity>();
}
}
Repository implementations
EFRepository
This is the repository that interacts with the database.
public class EFRepository<TEntity> : IRepository<TEntity> where TEntity : class, IKey
{
internal FetchDbContext _db;
internal DbSet<TEntity> _entities;
public EFRepository(FetchDbContext context)
{
_db = context;
_entities = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
int skip = -1,
int take = -1,
string includeProperties = "")
{
IQueryable<TEntity> query = _entities;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty.Trim());
}
if (orderBy != null)
{
query = orderBy(query);
}
if (skip >= 0)
{
query = query.Skip(skip);
}
if (take >= 0)
{
query = query.Take(take);
}
return query.ToList();
}
public virtual async Task<IEnumerable<TEntity>> GetAsync(
Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
int skip = -1,
int take = -1,
string includeProperties = "")
{
...
}
public virtual TEntity GetById(object id)
{
var result = _entities.Find(id);
if (result is null)
{
throw new KeyNotFoundException(nameof(id));
}
return result;
}
public virtual async Task<TEntity> GetByIdAsync(object id)
{
...
}
...
}
MemoryRepository
This repository saves the data in a dictionary instead of a database;
public class MemoryRepository<TEntity> : IRepository<TEntity> where TEntity : class, IKey
{
// Dictionary
Dictionary<Guid, TEntity> _entities = new();
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
int skip = -1,
int take = -1,
string includeProperties = "")
{
IQueryable<TEntity> query = _entities.Values.AsQueryable();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual async Task<IEnumerable<TEntity>> GetAsync(
Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
int skip = -1,
int take = -1,
string includeProperties = "")
{
...
}
public virtual TEntity GetById(object id)
{
Guid guid = (Guid)id;
var result = _entities.Values.FirstOrDefault(e => e.Id == guid);
if (result is null)
{
throw new KeyNotFoundException(nameof(id));
}
return result;
}
public virtual async Task<TEntity> GetByIdAsync(object id)
{
...
}
public virtual void Insert(TEntity entity)
{
if (entity.Id == default)
{
entity.Id = Guid.NewGuid();
}
_entities[entity.Id] = entity;
}
...
}
IDbContext
I have implemented the following methods on the DbContext class, and defined them as an interface.
public interface IDbContext
{
void Commit();
Task CommitAsync();
}
Interface of IRepository
public interface IRepository<TEntity> where TEntity : class, IKey
{
int Count(Expression<Func<TEntity, bool>>? filter = null);
Task<int> CountAsync(Expression<Func<TEntity, bool>>? filter = null);
void Delete(object id);
void Delete(TEntity entityToDelete);
Task DeleteAsync(object id);
IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null, int skip = -1, int take = -1,
string includeProperties = "");
Task<IEnumerable<TEntity>> GetAsync(Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null, int skip = -1, int take = -1,
string includeProperties = "");
TEntity GetById(object id);
Task<TEntity> GetByIdAsync(object id);
void Insert(TEntity entity);
Task InsertAsync(TEntity entity);
void Update(TEntity entityToUpdate);
}
Unit testing
In the unit test we can define a unit of work in the following way:
var dbContext = new MockDbContext();
var repositoryFactory = new MemoryRepositoryFactory();
var unitOfWork = new UnitOfWork(dbContext, repositoryFactory);
And we will pass that mocked instance of DB context
private class MockDbContext : IDbContext
{
public void Commit()
{
return;
}
public async Task CommitAsync()
{
await Task.Delay(0); // Probably something better applies here
}
}
I use this to create instances of the services in the unit tests so I can test the business logic in the services.
Caveats
Storing the entities in memory is a lot different than from a database. To give an example, if we have multiple nested entities, EF will create entities in the appropriate tables. However, this does not happen in the memory repository, so if you'd insert something in table A, and expect something in table B as well, then that will not be there!
If you have entities that derive from a common base entity, then this approach gives some issues to the reason above.
Hope this helps!
I am trying to implement the generic repository pattern in my application. I have two interfaces, IEntity and IRepository:
IEntity:
public interface IEntity
{
int Id { get; set; }
}
IRepository:
public interface IRepository<T> where T : IEntity
{
void AddOrUpdate(T ent);
void Delete(T ent);
IQueryable<T> GetAll();
}
And now I'm trying to make a generic RepositoryGlobal class, but I am getting this error:
The type 'T' must be a reference type in order to use it as parameter 'T' in the generic type or method
This is what my code looks like:
public class RepositoryGlobal<T> : IRepository<T> where T : IEntity
{
public RepositoryGlobal(DbContext _ctx)
{
this._context = _ctx;
}
private DbContext _context;
public void Add(T ent)
{
this._context.Set<T>().Add(ent);
}
public void AddOrUpdate(T ent)
{
if (ent.Id == 0)
{
//not important
}else
{
//for now
}
}
public void Delete(T ent)
{
}
public IQueryable<T> GetAll()
{
return null;
}
}
The error appears in the Add method of the RepositoryGlobal class.
Any ideas?
Thanks
You need to add a class constraint
public class RepositoryGlobal<T> : IRepository<T> where T : class, IEntity
I am reading about the Repository Pattern design. This article is really good but I don't understand a few things inside a constructor.
In this code:
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(int id);
}
/// <summary>
/// The EF-dependent, generic repository for data access
/// </summary>
/// <typeparam name="T">Type of entity for this Repository.</typeparam>
public class MyRepository<T> : IRepository<T> where T : class
{
public MyRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("Null DbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
public virtual T GetById(int id)
{
return DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = GetById(id);
if (entity == null) return; // not found; assume already deleted.
Delete(entity);
}
}
In its constructor, I read this:
DbContext = dbContext;
DbSet = DbContext.Set<T>();
Probably it means something but it doesn't make sense to me because DbContext is of a type not a variable storing a reference to an object. I always inherit from DbContext and create my own DbContext and then create an instance / element of it. Does it have any special meaning here? I am wondering why the author did not write these line in this way:
MyDbContext dbContext = new MyDbContext();
etc.
Thanks
Look further down the source code for that class and you will see:
protected DbContext DbContext { get; set; }
That's declaring a property of type DbContext which is also called DbContext. When you assign in the ctor, you are assigning to this property.
(The way that the scoping rules work in C# means that within the constructor itself, the DbContext resolves to the class's property of that name, not the type DbContext.)
Yes, it can be confusing. For this reason, it might have been better for the author to give the property a different name from its type - but this kind of naming is actually fairly common.
At the very least, the ctor should be rewritten thus:
public MyRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("Null DbContext");
this.DbContext = dbContext;
this.DbSet = this.DbContext.Set<T>();
}
I have taken over a project from a collegue, who uses the IRepository pattern. I have never used it before, so I have some problems understanding how to make a WHERE clause or an ANY clause.
Previously I had the following code, which uses the DataContext repository (the actual implementation, where I can use where clauses:
IQueryable<Office> officeList = repository.Offices;
if (specification.CountryId > 0)
{
officeList = repository.Offices.Where(c => c.CountryId == specification.CountryId);
}
if (specification.LetterSize != null)
{
officeList =
officeList.Where(
c => c.OfficeProducts.Any(d => d.OfficeProductDetail.Size == (int)specification.LetterSize));
}
return officeList.ToList();
I would like to understand how to get a code snippet like the one above to work using the IRepository pattern. I have tried to make an implementation of a WHERE/QUERY, but could not get it to work.
My question:
How do you implement a WHERE/ANY statement in practice, so I can do something like the code snippet above?
My IRepository:
public interface IRepository
{
T GetById<T>(long id) where T : class;
void Create<T>(T entity) where T : class;
void Update<T>(T entity) where T : class;
void SaveOrUpdate<T>(T entity) where T : class;
void Delete<T>(T entity) where T : class;
IList<T> FindAll<T>(params OrderBy[] orders) where T : class;
int Count<T>(Expression<Func<T, bool>> whereExpression) where T : class;
bool Exists<T>(Expression<Func<T, bool>> whereExpression) where T : class;
T FindFirst<T>(Expression<Func<T, bool>> whereExpression, params OrderBy[] orders) where T : class;
PaginatedResult<T> Find<T>(Expression<Func<T, bool>> whereExpression, int pageIndex, int pageSize, params OrderBy[] orders) where T : class;
void ExecuteNativeSQL(string sql);
}
The implementation:
public class EFRepository : IRepository
{
private IDBFactory databaseFactory;
private LetterAmazerContext dataContext;
public EFRepository(IDBFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
protected LetterAmazerContext DataContext
{
get { return dataContext ?? (dataContext = databaseFactory.Get()); }
}
public T GetById<T>(long id) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Find(id);
}
public void Create<T>(T entity) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
dbset.Add(entity);
}
public void Update<T>(T entity) where T : class
{
dataContext.Entry(entity).State = EntityState.Modified;
}
public void SaveOrUpdate<T>(T entity) where T : class
{
throw new NotImplementedException();
}
public void Delete<T>(T entity) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
dbset.Remove(entity);
}
public IList<T> FindAll<T>(params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
var query = dbset.Where(t => true);
query = ApplyOrders<T>(query, orders);
return query.ToList<T>();
}
public int Count<T>(Expression<Func<T, bool>> whereExpression) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Count<T>(whereExpression);
}
public bool Exists<T>(Expression<Func<T, bool>> whereExpression) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Count<T>(whereExpression) != 0;
}
public T FindFirst<T>(Expression<Func<T, bool>> whereExpression, params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
var query = dbset.Where(whereExpression);
query = ApplyOrders<T>(query, orders);
return query.SingleOrDefault<T>();
}
public PaginatedResult<T> Find<T>(Expression<Func<T, bool>> whereExpression, int pageIndex, int pageSize, params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
PaginatedResult<T> results = new PaginatedResult<T>();
var query = dbset.AsExpandable().Where(whereExpression);
query = ApplyOrders<T>(query, orders);
results.Results = query.Skip<T>(pageIndex * pageSize).Take<T>(pageSize).ToList<T>();
results.TotalItems = dbset.AsExpandable().LongCount(whereExpression);
return results;
}
public void ExecuteNativeSQL(string sql)
{
DataContext.Database.ExecuteSqlCommand(sql);
}
private IQueryable<T> ApplyOrders<T>(IQueryable<T> query, params OrderBy[] orders)
{
if (orders == null || orders.Length == 0) return query;
foreach (var order in orders)
{
query = query.OrderBy(order.ToString());
}
return query;
}
}
Your repository is at the moment open for arbitrary expressions, including potentially expressions that cannot be evaluated by the implementation.
On one hand this is a potential risk of not being able to deliver an implementation that matches so open contract.
On the other hand, why don't you just then expose it a bit more:
public interface IRepository
{
...
IQueryable<T> Query<T>();
}
and
public class EFRepository : IRepository
{
...
public IQueryable<T> Query<T>()
{
return DataContrxt.Set<T>();
}
}
Note that if you decide to go that way, most of your specific query methods doesn't make sense anymore as such open generic queryable interface satisfies most needs.
Note also that the same concern applies there, if you have multiple implementations you can't possibly guarantee that the contract is satisfied in the same way. Also, your potential client can easily create a query the provider will reject. If you accept these issues, the proposed solution solves your issue as you can now query the repository for almost anything.
And the last note, if you don't plan to have multiple implementations, remove the repository layer completely. The EF context is a unit of work with repositories inside. Starting from version 6, the context can be mocked so unit testing is possible.
i using below repository pattern. when i instance from the class getting this exception:
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
IQueryable<TEntity> GetQuery();
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
TEntity Single(Expression<Func<TEntity, bool>> predicate);
TEntity First(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void Delete(TEntity entity);
void Attach(TEntity entity);
void SaveChanges();
void SaveChanges(SaveOptions options);
}
public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
private ObjectContext _context;
private IObjectSet<TEntity> _objectSet;
public GenericRepository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<TEntity>();
}
public IQueryable<TEntity> GetQuery()
{
return _objectSet;
}
public IEnumerable<TEntity> GetAll()
{
return GetQuery().AsEnumerable();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _objectSet.Where<TEntity>(predicate);
}
public TEntity Single(Expression<Func<TEntity, bool>> predicate)
{
return _objectSet.Single<TEntity>(predicate);
}
public TEntity First(Expression<Func<TEntity, bool>> predicate)
{
return _objectSet.First<TEntity>(predicate);
}
public void Delete(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.DeleteObject(entity);
}
public void Add(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.AddObject(entity);
}
public void Attach(TEntity entity)
{
_objectSet.Attach(entity);
}
public void SaveChanges()
{
_context.SaveChanges();
}
public void SaveChanges(SaveOptions options)
{
_context.SaveChanges(options);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && _context != null)
{
_context.Dispose();
_context = null;
}
}
}
using:
var db = new AdventureWorks2012Entities();
IRepository<Person> person = new GenericRepository<Person>();
exception:
Problem is that you are using Entity Framework 4.1 or later, which has DbContext wrapper around ObjectContext. But your repository still usess ObjectContext. Add another constructor, which accepts DbContext:
public GenericRepository(DbContext context)
{
_context = (context as IObjectContextAdapter).ObjectContext;
_objectSet = _context.CreateObjectSet<TEntity>();
}
You can retrieve wrapped ObjectContext instance by casting DbContext to IObjectContextAdapter interface. Another option is updating your repository class to use latest DbContext, but that will require a little bit more coding, than simply adding one constructor:
public class GenericRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
private DbContext _context; // instead of ObjectContext
private DbSet<TEntity> _set; // instead of IObjectSet<TEntity>
public GenericRepository(DbContext context)
{
_context = context;
_set = _context.Set<TEntity>();
}
public IQueryable<TEntity> GetQuery()
{
return _set;
}
public IEnumerable<TEntity> GetAll()
{
return GetQuery().AsEnumerable();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _set.Where<TEntity>(predicate);
}
// etc
}
This is all in the error message, the first one is simple enough - your repository expects an ObjectContext parameter (which you don't provide).
The second one is basically saying that your db instance is not of type ObjectContext or at least can't be downcast to that particular type. If your sure AdventureWorks2012Entities derives from ObjectContext then you could do a direct cast e.g.
IRepository<Person> repo = new GenericRepository<Person>((ObjectContext)db);
You can pass a DbSet to your repository, so:
var db = new AdventureWorks2012Entities();
IRepository<Person> person = new GenericRepository<Person>(db.Person);
Where db.Person is declared in the AdventureWorks2012Entities DbContext class.
I'm developing a UnitOfWork/Repository abstraction layer to easily switch from NHibernate/EntityFramework/Raven and others and to better allow the tests.
Here the source code and here the Nuget package. I've to change the code base to better support DDD methodology, in particular:
remove the unit of work class, not needed because work with one aggregate per time
add the Commit method to the repository