How many repositories is too many? - asp.net-mvc

We are getting ready to modularize a large enterprise application that is currently using a dying stack of technologies. My question is in the Repository/Unit of Work pattern, how many Repositories can you have in a unit of work? Say for example we create a UnitOfWork on a single DbContext that exposes 50+ repository entities. Will this cause performance problems?
We were looking at maybe splitting it up so that each schema has it's own DbContext but this seems to add a lot of complexity and then doesn't allow for easy joining of data between the schemas. I feel like creating everything under one context/unit of work is the best answer for ease of use and maintainability but I am concerned performance may be a problem.
public class UnitOfWork : IUnitOfWork
{
private readonly AppContext _context;
public UnitOfWork(AppContext context)
{
_context = context;
Courses = new CourseRepository(_context);
Authors = new AuthorRepository(_context);
...
...
// Will lots of repositories here cause a performance problem?
...
...
...
...
...
}
public ICourseRepository Courses { get; private set; }
public IAuthorRepository Authors { get; private set; }
...
...
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}

It's a few years old now but I've used a UnitOfWork with a GenericRepository and not suffered any major performance issues. This is hooked into over 100 DB tables of a busy website.
That said, the website in question also employs the Dapper Micro-ORM throughout as it's very fast and gives more control on complex operations. For CRUD though, the setup below works well for me.
Unit Of Work
public class UnitOfWork :IDisposable
{
private DbContext _db = new DbContext();
private GenericRepository<Table1> table1Repository;
private GenericRepository<Table2> table2Repository;
private GenericRepository<Table3> table3Repository;
...
private GenericRepository<TableN> tableNRepository;
public GenericRepository<Table1> Table1Repository
{
get
{
if (this.table1Repository == null)
{
this.table1Repository = new GenericRepository<Table1>(_db);
}
return table1Repository;
}
}
public void Save()
{
_db.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Generic Repository
public class GenericRepository<TEntity> where TEntity :class
{
internal DbContext _db;
internal DbSet<TEntity> dbSet;
public GenericRepository(DbContext _db)
{
this._db = _db;
this.dbSet = _db.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
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);
}
else
{
return query;
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (_db.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
_db.Entry(entityToUpdate).State = EntityState.Modified;
}
}
Usage
var unitOfWork = new UnitOfWork();
List<Table1> list = unitOfWork.Table1Repository.Get(n => n.addedOn <= DateTime.Now);
Table1 item = unitOfWork.Table1Repository.GetById(1);
unitOfWork.Table1Repository.Insert(object);
unitOfWork.Save();
unitOfWork.Table1Repository.Update(object);
unitOfWork.Save();
unitOfWork.Table1Repository.Delete(1);
unitOfWork.Save();
unitOfWork.Table1Repository.Delete(object);
unitOfWork.Save();

Related

how to delete all records in a particular table before excel import using ASP.NET MVC

Please How do I delete all existing records, refresh and reset the table before I Save new records. I want to do it from my controller.
Controller
public ActionResult Create(ITEM items)
{
try
{
// TODO: Add insert logic here
if (ModelState.IsValid)
{
_itemsService.AddCity(thirdparties);
return RedirectToAction("Index");
}
}
catch
{
ModelState.AddModelError("", "We cannot add this items. Verify your data entries !");
}
return View(items);
}
I have an N-Tier layer
Infrastructure
Repository
Web(Presentation
Infrastructure
public interface IRepository<T> where T : class
{
IEnumerable<T> FindAll(Expression<Func<T, bool>> predicate = null);
T FindById(object id);
T FindByEntity(T entity);
T FindByString(object strValue);
void Add(T entity);
void Remove(T entity);
}
public abstract class Repository<T> : IRepository<T> where T : class
{
public virtual IEnumerable<T> FindAll(Expression<Func<T, bool>> predicate = null)
{
IQueryable<T> items = DataContextFactory.AdminEntities.Set<T>();
return (predicate == null) ? items : items.Where(predicate);
}
public abstract T FindById(object id);
public abstract T FindByString(object strValue);
public abstract T FindByEntity(T entity);
public virtual void Add(T entity)
{
DataContextFactory.AdminEntities.Set<T>().AddOrUpdate(entity);
DataContextFactory.AdminEntities.SaveChanges();
}
public virtual void Remove(T entity)
{
T item = FindByEntity(entity);
if (item != null)
DataContextFactory.AdminEntities.Set<T>().Remove(item);
DataContextFactory.AdminEntities.SaveChanges();
}
}
Repository
public class CitiesManager: ICitiesManager
{
private readonly IRepository<CITIES> _citiesRepository;
public CitiesManager(IRepository<CITIES> citiesRepository)
{
_citiesRepository = citiesRepository;
}
public CITIES City(Int32 cityID)
{
return _citiesRepository.FindById(cityID);
}
public IEnumerable<CITIES> Cities()
{
return _citiesRepository.FindAll().ToList();
}
public void AddCity(CITIES cities)
{
_citiesRepository.Add(cities);
}
public void RemoveCity(Int32 cityID)
{
_citiesRepository.Remove(City(cityID));
}
}
My model classes are in the infrastucture
Infrastructure --> Repository --> Web

An entity object cannot be referenced by multiple instances of IEntityChangeTracker when saving changes

I have two domain objects, both identical but with different PK properties:
public partial class Maintenance : MaintenanceBase
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int MaintenanceId { get; set; }
public virtual Employee Employee { get; set; }
}
public partial class MyMaintenance : MaintenanceBase
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int RowId { get; set; }
public virtual Employee Employee { get; set; }
}
The rest of the properties are inherited from a base class. The problem I am having is when try to call save changes in my post controller, I am getting the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
This is (basically) my controller method:
[HttpPost]
public ActionResult SubmitMyMaintenance(IList<MyMaintenance> myMaintenanceList, string userName)
{
foreach (var result in myMaintenanceList)
{
var m = iMyMaintenanceRepository.GetSingle(result.RowId);
Maintenance maintenance = new Maintenance();
// Use Injector to handle mapping between viewmodel and model
maintenance.InjectFrom(m);
try
{
if (ModelState.IsValid)
{
// save the maintenance item
iMaintenanceRepository.Add(maintenance);
iMaintenanceRepository.Save();
// delete the item in MyMaintenance
iMyMaintenanceRepository.Delete(m);
iMyMaintenanceRepository.Save();
}
}
catch (DataException ex)
{
message = ex.InnerException.ToString();
}
}
// refresh the view
var mvm = new MyMaintenanceListViewModel
{
MyMaintenanceList = iMyMaintenanceRepository.FindBy(v => v.CreatedBy.Equals(userName)).ToList(),
Message = "Your maintenance items were successfully added."
};
return View("MyMaintenance", mvm);
}
I suspect this is because I have instances of respositories (iMaintenanceRepository & iMyMaintenanceRepository) for both domain objects in the same controller post method, and both have a reference to the Employee entity.
For instance, when I dispose the iMyMaintenanceRepository and create a new instance (before refreshing the view at the end), I get en error about inserting a null value in the Employee table, which I am not inserting anything. That is the reason I suspect the Employee entity exists in two different data contexts. I am not sure how to resolve it though. None of the solutions I have found seem to apply and I am thinking it is more of an implementation problem on my part.
EDIT: Repositories
namespace EMMS.Models.Interfaces
{
public interface IMyMaintenanceRepository : IGenericRepository<MyMaintenance>
{
MyMaintenance GetSingle(int RowId);
}
}
namespace EMMS.Models.Repositories
{
public class MyMaintenanceRepository : GenericRepository<AppDBContext, MyMaintenance>, IMyMaintenanceRepository
{
public MyMaintenance GetSingle(int RowId)
{
var query = GetAll().FirstOrDefault(x => x.RowId == RowId);
return query;
}
}
}
namespace EMMS.ViewModels.Repositories
{
public class GenericRepository<C, T> : IDisposable, IGenericRepository<T>
where T : class
where C : DbContext, new()
{
private C _entities = new C();
public C Context
{
get { return _entities; }
set { _entities = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _entities.Set<T>();
return query;
}
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _entities.Set<T>().Where(predicate);
return query;
}
// enforce referential itegrity
public bool ValueInUse(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _entities.Set<T>().Where(predicate);
int count = query.Count();
return count > 0 ? true : false;
}
public virtual void Add(T entity)
{
_entities.Set<T>().Add(entity);
}
public virtual void Delete(T entity)
{
_entities.Entry(entity).State = System.Data.EntityState.Deleted;
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = System.Data.EntityState.Modified;
}
public virtual void Save()
{
_entities.SaveChanges();
}
private bool disposed = false; // to detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (_entities != null)
{
_entities.Dispose();
}
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
namespace EMMS.ViewModels.Interfaces
{
public interface IGenericRepository<T> where T : class
{
IQueryable<T> GetAll();
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
bool ValueInUse(System.Linq.Expressions.Expression<Func<T, bool>> predicate);
void Add(T entity);
void Delete(T entity);
void Edit(T entity);
void Save();
void Dispose();
}
}
You're absolute correct about the issue. Actually, in particular, it's because each repository has it's own instance of your context object, and you're trying to pass an Employee that was originally retrieved via one instance and saving it via different instance.
The easiest solution is to track all like things in one repository. In other words, just use one MaintenanceRepository can have calls to return both Maintenance and MyMaintenance. Though that stretches the idea of a "repository" a bit. This is why repositories are typically combined with a Unit of Work class, which would house the context for the repositories to share. However, at that point, you're basically just recreating the structure Entity Framework already implements. So, holding everything in just one "repository" makes more sense, but now you're really talking about a "service" pattern rather than a repository pattern. It's just semantics though.
UPDATE
Disclaimer: This is what I'm using currently in a project and it works for me. This may not be the best practice and reasonable people could very well disagree with my approach.
IService Interface
public interface IService<TContext, TEntity>
where TContext : DbContext
where TEntity : class
{
IEnumerable<TEntity> GetAll(
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
TEntity GetById(int id, string includeProperties = "");
TEntity GetOne(
Expression<Func<TEntity, bool>> filter = null,
string includeProperties = "");
TEntity GetFirst(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
TEntity GetLast(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
void Create(TEntity entity);
void Update(TEntity entity);
void Delete(int id);
void Delete(TEntity entity);
int Count(Expression<Func<TEntity, bool>> filter = null);
bool Any(Expression<Func<TEntity, bool>> filter = null);
}
Service, Implementation of IService
public class Service<TContext, TEntity> : IService<TContext, TEntity>
where TContext : DbContext
where TEntity : class
{
internal TContext context;
internal DbSet<TEntity> dbSet;
public Service(TContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> GetAll(
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
return Get(null, orderBy, includeProperties);
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
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.Distinct().ToList();
}
}
public virtual TEntity GetById(int id, string includeProperties = "")
{
return dbSet.Find(id);
}
public virtual TEntity GetOne(
Expression<Func<TEntity, bool>> filter,
string includeProperties = "")
{
return Get(filter, null, includeProperties).SingleOrDefault();
}
public virtual TEntity GetFirst(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
return Get(filter, orderBy, includeProperties).FirstOrDefault();
}
public virtual TEntity GetLast(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
return Get(filter, orderBy, includeProperties).LastOrDefault();
}
public virtual void Create(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(int id)
{
var entity = GetById(id);
Delete(entity);
}
public virtual void Delete(TEntity entity)
{
if (context.Entry(entity).State == EntityState.Detached)
{
dbSet.Attach(entity);
}
dbSet.Remove(entity);
}
public virtual void Update(TEntity entity)
{
if (context.Entry(entity).State == EntityState.Detached)
{
dbSet.Attach(entity);
}
context.Entry(entity).State = EntityState.Modified;
}
public virtual int Count(Expression<Func<TEntity, bool>> filter = null)
{
return Get(filter).Count();
}
public virtual bool Any(Expression<Func<TEntity, bool>> filter = null)
{
return Count(filter) > 0;
}
}
ServiceGroup, Abstract container for services
public abstract class ServiceGroup<TContext> : IDisposable
where TContext : DbContext
{
protected TContext context;
public virtual void Save()
{
try
{
context.SaveChanges();
}
catch (DbEntityValidationException validationException)
{
string validationErrorMessage = DbEntityValidationMessageParser.GetErrorMessage(validationException);
Console.WriteLine(validationErrorMessage);
}
}
#region Disposable
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
So, the way I use this all is whenever I want to create a collection of like things to work with, I subclass ServiceGroup like so:
public class SampleService : ServiceGroup<MyDbContext>
{
public SampleService()
{
this.context = new MyDbContext();
}
private Service<MyDbContext, SomeModel> someModels;
public Service<MyDbContext, SomeModel> SomeModels
{
get
{
if (someModels == null)
{
someModels = new Service<MyDbContext, SomeModel>(context);
}
return someModels;
}
}
private Service<MyDbContext, AnotherModel> anotherModels;
public Service<MyDbContext, AnotherModel> AnotherModels
{
get
{
if (anotherModels == null)
{
anotherModels = new Service<MyDbContext, AnotherModel>(context);
}
return anotherModels;
}
}
// rinse and repeat
}
This makes sure everything is using the same context instance. So to actually use it, you just do:
var service = new SampleService();
someModels = service.SomeModels.GetAll();
So I was searching the web for a complete example of a code first MVC implementation with repositories, unit of work, viewmodels, etc, and I found exactly what I was looking for here:
EFMVC - ASP.NET MVC 4, Entity Framework 5 Code First and Windows Azure
This is a great demo web app that does everything I need from an architectural standpoint. I don't understand half of it, though, (yet) and it took me about 4 hours of retooling my app, but man was it worth it! It also solved my IEntityChangeTracker error.

MVC3 EF Unit of Work + Generic Repository + Ninject

I'm new to MVC3 and have been following the awesome tutorials on the asp.net website. However, I can't quite wrap my head around how to use Unit of Work and Generic Repository patterns with Ninject. I used this tutorial as a starting point: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Without using interfaces, I know I can implement it like so:
Generic Repository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class
{
internal MyContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(MyContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
}
Unit of Work:
private MyContext context = new MyContext();
private GenericRepository<Student> studentRepository;
private GenericRepository<Course> courseRepository;
public GenericRepository<Student> StudentRepository
{
if (this.studentRepository == null)
{
this.studentRepository = new GenericRepository<Student>(context);
}
return studentRepository;
}
public GenericRepository<Course> CourseRepository
{
if (this.courseRepository == null)
{
this.courseRepository = new GenericRepository<Course>(context);
}
return courseRepository;
}
This setup allows me to pass the same context to all repositories, and then call a single Save() function to commit the changes.
I know I can use an interface IGenericRepository<TEntity> and the concrete implementation GenericRepository<TEntity> and then bind them using Ninject:
kernel.Bind(typeof(IGenericRepository<>)).To(typeof(GenericRepository<>));
But how would I go about setting up my IUnitOfWork and UnitOfWork to ensure that all my repositories share a single database context? Am I even doing it right in the first place? I've searched around but all I seem to find are tutorials that only use generic repositories without a unit of work.
Your base Repo:
public class BaseRepository<TObject> : IRepository<TObject>
where TObject : class
{
public BaseRepository(IUnitOfWork unitOfWork)
{
if (unitOfWork == null) throw new ArgumentException("unitOfWork");
UnitOfWork = unitOfWork;
}
protected DbSet<TObject> DbSet
{
get
{
return Context.Set<TObject>();
}
}
public void Dispose()
{
if (sharedContext && (Context != null))
Context.Dispose();
}
public virtual IQueryable<TObject> All()
{
return DbSet.AsQueryable();
}
public virtual IQueryable<TObject>
Filter(Expression<Func<TObject, bool>> predicate)
{
return DbSet.Where(predicate).AsQueryable<TObject>();
}
public virtual IQueryable<TObject> Filter<Key>(Expression<Func<TObject, Key>> sortingSelector, Expression<Func<TObject, bool>> filter, out int total,
SortingOrders sortby = SortingOrders.Asc, int index = 0, int size = 50)
{
int skipCount = index * size;
var _resultSet = filter != null ? DbSet.Where(filter).AsQueryable() : DbSet.AsQueryable();
total = _resultSet.Count();
_resultSet = sortby == SortingOrders.Asc ? _resultSet.OrderBy(sortingSelector).AsQueryable() : _resultSet.OrderByDescending(sortingSelector).AsQueryable();
_resultSet = skipCount == 0 ? _resultSet.Take(size) : _resultSet.Skip(skipCount).Take(size);
return _resultSet;
}
public bool Contains(Expression<Func<TObject, bool>> predicate)
{
return DbSet.Count(predicate) > 0;
}
public virtual TObject Find(params object[] keys)
{
return DbSet.Find(keys);
}
public virtual TObject Find(Expression<Func<TObject, bool>> predicate)
{
return DbSet.FirstOrDefault(predicate);
}
public virtual TObject Create(TObject TObject, bool SaveChanges = true)
{
var newEntry = DbSet.Add(TObject);
if (!sharedContext && SaveChanges)
Context.SaveChanges();
return newEntry;
}
public virtual int Count
{
get
{
return DbSet.Count();
}
}
public virtual int Delete(TObject TObject)
{
DbSet.Remove(TObject);
if (!sharedContext)
return Context.SaveChanges();
return 0;
}
public virtual int Update(TObject TObject, bool SaveChanges = true)
{
var entry = Context.Entry(TObject);
DbSet.Attach(TObject);
entry.State = EntityState.Modified;
if (!sharedContext && SaveChanges)
return Context.SaveChanges();
return 0;
}
public virtual int Delete(Expression<Func<TObject, bool>> predicate)
{
var objects = Filter(predicate);
foreach (var obj in objects)
DbSet.Remove(obj);
if (!sharedContext)
return Context.SaveChanges();
return 0;
}
/// <summary>
/// Sets the state of an entity.
/// </summary>
/// <param name="entity">object to set state.</param>
/// <param name="entityState"><see cref="EntityState"/></param>
protected virtual void SetEntityState(object entity, EntityState entityState)
{
Context.Entry(entity).State = entityState;
}
/// <summary>
///
/// </summary>
/// <param name="entity"></param>
protected virtual void Attach(object entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
Context.Entry(entity).State = EntityState.Modified;
}
protected virtual void Detach(object entity)
{
Context.Entry(entity).State = EntityState.Detached;
}
public void SubmitChanges()
{
UnitOfWork.SaveChanges();
}
#region Properties
private bool sharedContext { get; set; }
/// <summary>
/// Unit of work controlling this repository.
/// </summary>
protected IUnitOfWork UnitOfWork { get; set; }
/// <summary>
/// Provides access to the ef context we are working with
/// </summary>
internal IMyContext Context
{
get
{
return (IMyContext)UnitOfWork;
}
}
#endregion
}
Notice Context is an interface implementing UnitOfWork.
Your context interface:
public interface IMyContext : IDbContext
{
DbSet<Sometype> SomeProperty { get; set; }
...
}
Your IDbContext interface:
public interface IDbContext
{
DbChangeTracker ChangeTracker { get; }
DbContextConfiguration Configuration { get; }
Database Database { get; }
void Dispose();
void Dispose(bool disposing);
DbEntityEntry Entry(object entity);
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
bool Equals(object obj);
int GetHashCode();
Type GetType();
IEnumerable<DbEntityValidationResult> GetValidationErrors();
void OnModelCreating(DbModelBuilder modelBuilder);
int SaveChanges();
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbSet Set(Type entityType);
bool ShouldValidateEntity(DbEntityEntry entityEntry);
string ToString();
DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items);
}
Then the actual context implementation:
public class MyContext : DbContext, IUnitOfWork, IMyContext
{
//public MyContext()
//{
// Database.SetInitializer<ReconContext>(null);
//}
public ReconContext()
: base("Name=ReconContext")
{
((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = false;
}
public DbSet<SomeType> SomeProperty { get; set; }
....
public new void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SomePropertyMap());
.....
base.OnModelCreating(modelBuilder);
}
int IUnitOfWork.SaveChanges()
{
return base.SaveChanges();
}
void IDisposable.Dispose()
{
base.Dispose();
}
public new void Dispose(bool disposing)
{
base.Dispose(disposing);
}
public new bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return base.ShouldValidateEntity(entityEntry);
}
public new DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
return base.ValidateEntity(entityEntry, items);
}
}
Then in your ninject config you simply do:
kernel.Bind<IUnitOfWork<MyContext>>().To<MyContext>().InRequestScope();

asp.net mvc ef codefirst repository concurrency not working

I have an asp.net mvc application where I use EF CodeFirst with a repository pattern and am trying to test and catch a DbUpdateConcurrencyException, but it doesn't appear to be firing, evening though when I update a record in one browser, while it is already open in another, and then go to the other and try to update the same record, it allows me to do it. I have a TimeStamp column on the table (called RowVersion) which does change with every update, so that is correct, and from watching sql profiler I can see the update statement does check that the timestamp is what is being passed in, so I'm out of ideas, and am unable to find anything so far to really help me. If anyone can see something I am doing wrong, please let me know. Thanks!!
Here is my entity:
public class MyEntity : EntityBase
{
public override int Id { get; set; }
public DateTime? LastEdited { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
Here is the repository and it's base classes/dbcontext:
public class MyEntityDataProvider : EfDataProviderBase<MyEntity>, IMyEntityDataProvider
{
public MyEntityDataProvider (IDataContext dataContext) : base(dataContext) { }
}
public abstract class EfDataProviderBase<T> : DataProviderBase<T> where T : EntityBase
{
public new DbDataContext DataContext
{
get { return base.DataContext as DbDataContext; }
protected set { base.DataContext = value; }
}
public EfDataProviderBase(IDataContext dataContext)
: base(dataContext)
{
if (!(dataContext is DbDataContext)) throw new ArgumentException("Parameter 'dataContext' must be of type DbDataContext.");
}
public override T GetById(int id)
{
if (id < 1) return null;
return this.DataContext.GetDbSet<T>().Find(id);
}
public override List<T> GetAll()
{
return this.DataContext.GetDbSet<T>().OrderBy(e => e.Id).ToList();
}
public override void Insert(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
this.DataContext.GetDbSet<T>().Add(entity);
}
public override void Update(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
if (!this.DataContext.GetDbSet<T>().Local.Any(e => e.Id == entity.Id))
this.DataContext.GetDbSet<T>().Attach(entity);
this.DataContext.Entry(entity).State = EntityState.Modified;
}
}
public class DbDataContext : DbContext, IDataContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<T> GetDbSet<T>() where T : EntityBase
{
Type entityType = typeof(T);
if (entityType == typeof(User))
return this.Users as DbSet<T>;
}
public void Commit()
{
this.SaveChanges();
}
}
public abstract class DataProviderBase<T> : IDataProvider<T> where T: EntityBase
{
public IDataContext DataContext { get; protected set; }
public DataProviderBase(IDataContext dataContext)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
this.DataContext = dataContext;
}
public abstract T GetById(int id);
public abstract List<T> GetAll();
public void Save(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
if (entity.IsNew)
{
this.Insert(entity);
}
else if (entity.IsDeleted)
{
this.Delete(entity);
}
else
{
this.Update(entity);
}
}
public abstract void Insert(T entity);
public abstract void Update(T entity);
}
Here is my controller action:
[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult UpdateViewModel(ViewModel model)
{
if (ModelState.IsValid)
{
try
{
MyEntity current = this.MyEntityDataProvider.GetById(model.Id);
current.LastEdited = DateTime.UtcNow;
current.RowVersion = model.Timestamp; // should have original timestamp, getting from hidden field on view
this.MyEntityDataProvider.Save(current);
this.DataContext.Commit(); // one dbcontext per http request, injected by ninject
return Json(new { success = "Saved!" });
}
catch (DbUpdateConcurrencyException)
{
ModelState.AddModelError("", "This record has been edited by an another person since you have opened it - please close and re-open to attempt your changes again.");
}
}
return Json(new { errors = GetErrorsFromModelState() });
}
Good morning friend.
You should add information on the property you want to control concurrency.
Example:
[ConcurrencyCheck]
public int Code {get; set;}

How to build a generic repository

I'm developing a web application in ASP.NET MVC with NHibernate.
Based in articles and tutorials I've found at Google, I'm using Repository for my classes.
I have 10 classes and 10 repositories. Today I figured out that 90% of mine repositories are exactly equal each other, except for the class. This is one example:
public class PromocaoRepository:IPromocaoRepository {
private ISession Session;
public PromocaoRepository() {
this.Session = NHibernateSessionFactory.OpenSession();
}
public void Add(Promocao promocao) {
using(ITransaction transaction = this.Session.BeginTransaction()) {
this.Session.Save(promocao);
transaction.Commit();
}
}
public void Edit(Promocao promocao) {
using(ITransaction transaction = this.Session.BeginTransaction()) {
this.Session.Update(promocao);
transaction.Commit();
}
}
public void Remove(Promocao promocao) {
using(ITransaction transaction = this.Session.BeginTransaction()) {
this.Session.Delete(promocao);
transaction.Commit();
}
}
public Promocao GetById(int id) {
return this.Session.Get<Promocao>(id);
}
}
There is a way to do a kind of generic repository witch I can use in all my classes?
If it's possible, what should I do in case I need to create a particular method for an specific class?
From another thread:
public interface IRepository<T> : IQueryable<T>
{
void Add(T entity);
T Get(Guid id);
void Remove(T entity);
}
public class Repository<T> : IQueryable<T>
{
private readonly ISession session;
public Repository(ISession session)
{
session = session;
}
public Type ElementType
{
get { return session.Query<T>().ElementType; }
}
public Expression Expression
{
get { return session.Query<T>().Expression; }
}
public IQueryProvider Provider
{
get { return session.Query<T>().Provider; }
}
public void Add(T entity)
{
session.Save(entity);
}
public T Get(Guid id)
{
return session.Get<T>(id);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return session.Query<T>().GetEnumerator();
}
public void Remove(T entity)
{
session.Delete(entity);
}
}
Wrapping up nHibernate in repositories
That being said, you should take a look at S#arp Architecture, which already helps you with this repetitive process using Dependency Injection.
You should make a generic repository, which you can use in the general case, and if any extra methods is needed for a particular class, add it by using inheritance. Using your example:
public class GenericRepository<TEntity> :IGenericRepository<TEntity> {
private ISession Session;
public GenericRepository() {
this.Session = NHibernateSessionFactory.OpenSession();
}
public void Add(TEntity instance) {
using(ITransaction transaction = this.Session.BeginTransaction()) {
this.Session.Save(instance);
transaction.Commit();
}
}
/* other methods */
}
public class SpecificRepository : GenericRepository<SpecificEntity>, ISpecificRepository
{
public void SpecialQuery() { /* added method implementation */ }
}
Here is my answer to a similar question (28 votes as of right now):
Advantage of creating a generic repository vs. specific repository for each object?
The idea is to genericize the implementation, not the interface. Instead of an outward-facing generic repository interface, create an inward-facing generic repository base class, which you use to easily implement entity-specific interfaces.
Edit: I should point out that generic repositories serve a very different function than specific repositories. Repositories are intended to encapsulate the data access mechanisms behind the entity's queries, including all of the query logic. A generic repository encapsulates the ability to create queries, but it doesn't encapsulate any specific query about an entity.
The point is to not make repository consumers responsible for writing their own queries. A generic repository lives at the same level of abstraction as an ORM; a specific repository lives at a level above that.
Here's another example of a generic repository
Have a look at my answer to the question "Asp.net MVC 2 Entity Framework Generic Repository Method. how to Update a specific Collumn" - it should give you a good idea of what to do.
HTHs,
Charles
E.g.
Base model:
public interface IDbTable
{
int Id { get; set; }
DateTime DateCreated { get; set; }
DateTime DateUpdated { get; set; }
}
public class DbTable
{
public int Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateUpdated { get; set; }
}
Your model
public class Category : DbTable
{
public string Name { get; set; }
}
Your repository
public interface IBaseRepository<T> where T : class, IDbTable
{
void Add<T>(T entity);
void Edit<T>(T entity);
void Remove<T>(T entity);
T GetById(int id);
}
public class BaseRepository<T> : IBaseRepository<T>
{
private ISession Session;
public BaseRepository()
{
this.Session = NHibernateSessionFactory.OpenSession();
}
public void Add(T entity)
{
entity.DateCreated = DateTime.UtcNow;
entity.DateUpdated = DateTime.UtcNow;
using(ITransaction transaction = this.Session.BeginTransaction())
{
this.Session.Save(entity);
transaction.Commit();
}
}
public void Edit(T entity)
{
entity.DateUpdated = DateTime.UtcNow;
using(ITransaction transaction = this.Session.BeginTransaction())
{
this.Session.Update(entity);
transaction.Commit();
}
}
public void Remove(T entity)
{
using(ITransaction transaction = this.Session.BeginTransaction())
{
this.Session.Delete(entity);
transaction.Commit();
}
}
public T GetById(int id)
{
return this.Session.Get<T>(id);
}
}
Oh, and lets not forget the concrete implementation
public interface ICategoryRepository : IBaseRepository<Category>
{
Category GetCategoryByName(string categoryName);
}
public CategoryRepository : BaseRepository<Category>
{
public Category GetCategoryByName(string categoryName)
{
//blah
}
}

Resources