0

我有两个域对象,它们都相同但具有不同的 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; }
}

其余属性继承自基类。我遇到的问题是当尝试在我的后控制器中调用保存更改时,我收到以下错误:

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

}

我怀疑这是因为我在同一个控制器 post 方法中有两个域对象的存储库 ( iMaintenanceRepository& iMyMaintenanceRepository) 实例,并且都引用了 Employee 实体。

例如,当我处理 iMyMaintenanceRepository 并创建一个新实例(在最后刷新视图之前)时,我收到关于在 Employee 表中插入空值的错误,我没有插入任何东西。这就是我怀疑 Employee 实体存在于两个不同的数据上下文中的原因。我不知道如何解决它。我发现的所有解决方案似乎都不适用,我认为这更多是我的实施问题。

编辑:存储库

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

2 回答 2

2

你对这个问题是绝对正确的。实际上,特别是,这是因为每个存储库都有自己的上下文对象实例,并且您试图传递一个Employee最初通过一个实例检索并通过不同实例保存它的实例。

最简单的解决方案是在一个存储库中跟踪所有类似的东西。换句话说,只需使用 one MaintenanceRepositorycan have calls 来返回Maintenanceand MyMaintenance。虽然这有点延伸了“存储库”的概念。这就是存储库通常与工作单元类结合的原因,该类将容纳存储库共享的上下文。但是,此时,您基本上只是重新创建 Entity Framework 已经实现的结构。因此,将所有内容都放在一个“存储库”中更有意义,但现在您实际上是在谈论“服务”模式而不是存储库模式。但这只是语义。

更新

免责声明:这是我目前在项目中使用的,它对我有用。这可能不是最佳实践,理性的人很可能不同意我的方法。

IService界面

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, 实施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, 服务的抽象容器

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
}

所以,我使用这一切的方式是,每当我想创建一个可以使用的类似事物的集合时,我都会ServiceGroup像这样子类化:

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();
于 2013-08-15T15:50:55.047 回答
0

所以我在网上搜索代码优先 MVC 实现的完整示例,其中包含存储库、工作单元、视图模型等,我在这里找到了我想要的东西:

EFMVC - ASP.NET MVC 4、Entity Framework 5 Code First 和 Windows Azure

这是一个很棒的演示网络应用程序,从架构的角度来看,它可以满足我的一切需求。不过,我不明白其中的一半,(还)我花了大约 4 个小时来重新调整我的应用程序,但人是值得的!它还解决了我的 IEntityChangeTracker 错误。

于 2013-08-16T04:24:41.580 回答