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

时间:2016-09-24 16:52:58

标签: c# entity-framework asp.net-core asp.net-core-mvc entity-framework-core

我正在使用.net核心。

  

我的目标: 我希望Edit之后能够Creating成为SalesOrder。

现在我可以创建和编辑。但它正在抛出错误

  

无法跟踪实体类型'SalesOrder'的实例,因为   另一个具有相同密钥的此类实例已经存在   跟踪。添加新实体时,对于大多数键类型都是唯一的   如果没有设置密钥,则将创建临时密钥值(即,如果密钥   属性被指定为其类型的默认值)。如果你是   明确设置新实体的关键值,确保不这样做   与现有实体或为其他实体生成的临时值发生冲突   新实体。附加现有实体时,请确保只有一个实体   具有给定键值的实体实例附加到上下文。

当我在创建后尝试编辑时。

我的保存()功能:

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

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

1 个答案:

答案 0 :(得分:-1)

你说你明白了这个问题......所以解决办法是从数据库中获取原始实体并直接更新其属性,然后自行更新。我的意思是你需要做的是避免打电话

    context.Update(entity);

其中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

的列表