繁体   English   中英

无法跟踪实体类型“ SalesOrder”的实例,因为已经跟踪了具有相同键的该类型的另一个实例

[英]The instance of entity type 'SalesOrder' cannot be tracked because another instance of this type with the same key is already being tracked

我正在使用.net core。

我的目标:我希望能够在Creating之后立即Edit SalesOrder。

现在,我可以创建和编辑了。 但是它抛出一个错误

无法跟踪实体类型“ SalesOrder”的实例,因为已经跟踪了具有相同键的该类型的另一个实例。 添加新实体时,对于大多数键类型,如果未设置任何键(即,如果为键属性分配了其类型的默认值),则将创建唯一的临时键值。 如果您为新实体明确设置键值,请确保它们不与现有实体或为其他新实体生成的临时值冲突。 附加现有实体时,请确保仅将一个具有给定键值的实体实例附加到上下文。

当我尝试创建后立即进行编辑时。

我的Save()函数:

public class SalesOrdersController : Controller
{
    private readonly ApplicationDbContext _dbContext;
    public SalesOrdersController(ApplicationDbContext dbContext){
        _dbContext = dbContext;
    }
    // ...other Controller actions
    public JsonResult Save([FromBody]SalesOrderViewModel salesOrderViewModel)
    {
        SalesOrder salesOrder = new SalesOrder();
        salesOrder.document_id = salesOrderViewModel.document_id;
        salesOrder.customer = salesOrderViewModel.customer;
        salesOrder.document_status_id = salesOrderViewModel.document_status_id;
        ...
        salesOrder.object_state = salesOrderViewModel.object_state;

        _dbContext.Entry(salesOrder).State = Helpers.ConvertState(salesOrder.object_state);
        _dbContext.SaveChanges();

        salesOrderViewModel.document_id = salesOrder.document_id;
        salesOrderViewModel.object_state = ObjectState.Unchanged;
        return Json(new { salesOrderViewModel });
    }
}

以及根据请求更新状态的功能:

public static EntityState ConvertState(ObjectState objectState){
    switch (objectState){
        case ObjectState.Added:
            return EntityState.Added;
        case ObjectState.Modified:
            return EntityState.Modified;
        case ObjectState.Deleted:
            return EntityState.Deleted;
        default:
            return EntityState.Unchanged;
    }
}

我知道在创建后刷新实体状态是一个问题。 我该如何解决该错误?

您说您了解问题...因此解决方案是从数据库中获取原始实体并直接更新其属性,然后再对其进行更新。 我的意思是你需要避免打电话

    context.Update(entity);

其中实体是模型中的对象。

因此,一种解决方案将是类似于以下内容的解决方案,我同意这可能不是解决问题的最佳方法。

假设您使用的是通用存储库(这比非通用存储库难,因为您事先不知道字段)

public void Edit(TBusinessObject entity)
        {

        var originalEntity = context.Set<TBusinessObject>().AsNoTracking().FirstOrDefault(r => r.Id.Equals(entity.Id));
        EntityEntry<TBusinessObject> original = context.Entry(originalEntity);
        EntityEntry<TBusinessObject> client = context.Entry(entity);
        foreach (var property in original.OriginalValues.Properties)
        {
            var dbMember = original.Member(property.Name);
            var clientMember = client.Member(property.Name);
            if(!property.IsPrimaryKey() && dbMember.CurrentValue != clientMember.CurrentValue && clientMember.CurrentValue!= null)
            {
                dbMember.CurrentValue = clientMember.CurrentValue;
                dbMember.IsModified = true;
            }
        }
        context.Update(originalEntity);
        context.SaveChanges(true);
    }

同样,可以优化此代码,如果它不是通用存储库(您可以在其中知道字段的名称和类型),则可以更简单。

更新1:我发现EF.Core尚未完全具备EF6支持的所有功能。 但是,它还是某种程度上倾向于现代开发实践。您发布的示例全部关于使用EF.Core来实现传统的存储库思想。 如果切换到使用UnitOfWork或CQRS,您将不会遇到这些问题,更新和CRUS之类的更改通常将变得前所未有地流畅。 我将一个对象传递给上下文,并且上下文本身可以弄清楚它属于哪个表以及如何处理它。 因此,我建议更改选择使用EF.Core的方式

试试这个最简单的实现:

public void Commit()
{
    using (var context = new ApplicationDbContext())
    {
            context.UpdateRange(Changed);
            context.AddRange(Added);
            context.RemoveRange(Deleted);

            context.SaveChanges();
            ClearAllChanges();
    }
}

“更改,添加,删除”只是列出您可能考虑使用AsynchronousBags的列表

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM