首页 文章

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

提问于
浏览
0

我正在使用.net核心 .

我的目标:我希望能够在创建后立即编辑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;
    }
}

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

1 回答

  • -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的列表

相关问题