Asp.net mvc 保存更改时,一个实体对象不能被IEntityChangeTracker的多个实例引用

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

我有两个域对象,都相同,但PK属性不同:

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