实体框架核心。如何正确更新相关数据?

Nik*_*rov 3 c# one-to-many sql-update entity-framework-core

这个问题很常见,但我还是不明白如何正确更新相关实体?

我有以下代码:

    public async Task<bool> UpdateStepAssignedToLevelAsync(Step step, Guid levelId, int priority = -1)
    {
        var item = await this._context.StepLevels
            .Include(sl => sl.Step)
            .FirstOrDefaultAsync(x => x.StepId == step.Id && x.LevelId == levelId);
        if (item == null)
        {
            return false;
        }

        //this._context.Entry(item).State = EntityState.Detached;
        if (priority > -1)
        {
            item.Priority = priority;
        }

        item.Step = step;

        //this._context.StepLevels.Update(item);
        var rows = await this._context.SaveChangesAsync();
        return rows > 0;
    }
Run Code Online (Sandbox Code Playgroud)

当它运行时,我收到以下错误:

InvalidOperationException: The instance of entity type 'Step' cannot be tracked because another instance with the key value '{Id: 35290c18-5b0a-46a5-8f59-8888cf548df5}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Run Code Online (Sandbox Code Playgroud)

据我了解,自从方法开始时的选择请求以来,正在跟踪实体。好的,但是当我分离实体并调用 Update 方法时(见注释行),Step 实体没有被改变。但 StepLevel 确实如此:优先级正在改变。当我尝试只调用 Update 时,EF 会尝试插入新的 Step 而不是更新现有的 Step。

所以,请你给我建议,这里最好的练习是什么?

非常感谢!

Iva*_*oev 7

首先,分离实体不会分离相关实体,因此分离item不会分离item.Step检索到的.Include(sl => sl.Step).

其次,由于您不想更改item.Step,而是要更新现有Step实体 ( x.StepId == step.Id),并且您知道上下文正在跟踪(包含)Step从数据库加载的相应实体,因此您应该使用更新数据库实体的过程通过Entry(...).CurrentValues.SetValues方法从分离的实体。

所以删除

item.Step = step;
Run Code Online (Sandbox Code Playgroud)

并改用以下内容:

this._context.Entry(item.Step).CurrentValues.SetValues(step);
Run Code Online (Sandbox Code Playgroud)

最后一点。当您使用附加实体时,不需要(不应该)使用Update方法。如果任何被跟踪的实体,更改跟踪器将自动检测更改的属性值。