在对象图中查找具有相同键的实体,以防止"ObjectStateManager中已存在具有相同键的对象"错误

Mas*_*oud 5 c# entity-framework poco ef-code-first dbcontext

我首先使用EF代码开发我的3层WinForm应用程序,我使用了断开连接的POCOs作为我的模型实体.我的所有实体都是从BaseEntity类继承的.

我使用了断开连接的POCOs,所以我State在客户端处理实体,并且在ApplyChanges()方法中,我将我的实体图(例如An Orderwith it's OrderLinesand Products)附加到我DbContext,然后将每个实体State与它的客户端同步State.

public class BaseEntity
{

    int _dataBaseId = -1;

    public virtual int DataBaseId // DataBaseId override in each entity to return it's key
    {
        get { return _dataBaseId; }
    } 

    public States State { get; set; }

    public enum States
    {
        Unchanged, 
        Added,
        Modified,
        Deleted
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,当我想保存相关实体的图形时,我使用了以下方法:

    public static EntityState ConvertState(BaseEntity.States state)
    {
        switch (state)
        {
            case BaseEntity.States.Added:
                return EntityState.Added;
            case BaseEntity.States.Modified:
                return EntityState.Modified;
            case BaseEntity.States.Deleted:
                return EntityState.Deleted;
            default:
                return EntityState.Unchanged;
        }
    }

    public void ApplyChanges<TEntity>(TEntity root) where TEntity : BaseEntity
    {
       _dbContext.Set<TEntity>().Add(root);
        foreach (var entry in _dbContext.ChangeTracker
        .Entries<BaseEntity>())
        {
            BaseEntity stateInfo = entry.Entity;
            entry.State = ConvertState(stateInfo.State);
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是如果我的图包含2个或更多具有相同键的实体,则会出现此错误:

An object with the same key already exists in the ObjectStateManager...
Run Code Online (Sandbox Code Playgroud)

如何在我的图形(root)中检测具有相同键的实体并使其在我的ApplyChanges()方法中是唯一的?

Mas*_*oud 0

我改变了我BaseEntity

public class BaseEntity
{
   public int Id {get; set;}
   public States State { get; set; }
   public bool MustDelete {get; set;} 

   public enum States
   {
     Unchanged, 
     Added,
     Modified,
     Deleted
   }
}
Run Code Online (Sandbox Code Playgroud)

并且还更改了我的 BaseDomainService<T>班级中的以下方法:

public class BaseDomainService<T> where T : class
{
    protected readonly DbContext _dbContext;

    public BaseDomainService(IUnitOfWork uow)
    {
        _dbContext = (DbContext)uow;
    }
    .....


    public static EntityState ConvertState(BaseEntity.States state)
    {
        switch (state)
        {
            case BaseEntity.States.Added:
                return EntityState.Added;
            case BaseEntity.States.Modified:
                return EntityState.Modified;
            case BaseEntity.States.Deleted:
                return EntityState.Deleted;
            default:
                return EntityState.Unchanged;
        }
    }    

    public void ApplyChanges<TEntity>(TEntity root) where TEntity : BaseEntity
    {
        _dbContext.Set<TEntity>().Add(root);
        foreach (var entry in _dbContext.ChangeTracker
        .Entries<BaseEntity>())
        {
            if (FoundAnEntityWithSameKeyInDbContext<TEntity>(entry))
                entry.State = EntityState.Detached;
            else
            {
                BaseEntity stateInfo = entry.Entity;
                if (stateInfo.MustDelete == true)
                    entry.State = EntityState.Detached;
                else
                    entry.State = ConvertState(stateInfo.State);
            }
        }
    }

    private bool FoundAnEntityWithSameKeyInDbContext<TEntity>(DbEntityEntry<BaseEntity> entry) where TEntity : BaseEntity
    {
        var tmp = _dbContext.ChangeTracker.Entries<BaseEntity>().Count(t => t.Entity.Id == entry.Entity.Id && t.Entity.Id != 0 && t.Entity.GetType() == entry.Entity.GetType());
        if (tmp > 1)
            return true;
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,问题就解决了。