Asp.net mvc 保存更改时,一个实体对象不能被IEntityChangeTracker的多个实例引用
我有两个域对象,都相同,但PK属性不同:Asp.net mvc 保存更改时,一个实体对象不能被IEntityChangeTracker的多个实例引用,asp.net-mvc,controller,ef-code-first,repository,http-post,Asp.net Mvc,Controller,Ef Code First,Repository,Http Post,我有两个域对象,都相同,但PK属性不同: public partial class Maintenance : MaintenanceBase { [Key] [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] public int MaintenanceId { get; set; } public virtual Employee Employee { get; set; } } p
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; }
}
其余属性从基类继承。我遇到的问题是,当尝试在我的post controller中调用save changes时,我遇到以下错误:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
这是(基本上)我的控制器方法:
[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);
}
你对这个问题的看法完全正确。事实上,特别是,这是因为每个存储库都有自己的上下文对象实例,并且您试图传递一个
Employee
,它最初是通过一个实例检索的,并通过不同的实例保存
最简单的解决方案是在一个存储库中跟踪所有类似的内容。换句话说,只需使用一个maintenancespository
即可调用以返回Maintenance
和MyMaintenance
。尽管这有点延伸了“存储库”的概念。这就是为什么存储库通常与一个工作类单元结合在一起,这个工作类单元将容纳存储库共享的上下文。然而,此时,您基本上只是重新创建已经实现的结构实体框架。因此,将所有内容保存在一个“存储库”中更有意义,但现在您真正谈论的是“服务”模式,而不是存储库模式。不过这只是语义学
更新
免责声明:这是我目前在一个项目中使用的,它对我有效。这可能不是最好的做法,理性的人很可能不同意我的方法
iSeries设备
接口
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);
}
服务组
,服务的抽象容器
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();
}
}
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
}
这确保所有内容都使用相同的上下文实例。因此,要真正使用它,您只需执行以下操作:
var service = new SampleService();
someModels = service.SomeModels.GetAll();
因此,我在网上搜索代码优先的MVC实现的完整示例,其中包括存储库、工作单元、视图模型等,我在这里找到了我想要的:
这是一个很棒的演示web应用程序,可以从架构的角度完成我需要的一切。虽然我不懂一半,但我花了大约4个小时的时间重新装备我的应用程序,但这是值得的!它还解决了我的IEntityChangeTracker错误。谢谢Chris。我将不得不考虑合并这两个存储库。我编辑了我的文章,包括两个存储库和它们实现的通用存储库。如果您对如何组合它们有任何建议,我将不胜感激。太棒了!再次感谢克里斯。我会看看你的解决方案。
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
}
var service = new SampleService();
someModels = service.SomeModels.GetAll();