【问题标题】:Entity Framework Core 5: The different between saving use Add(oldItem) and db.Entry(oldItem).State = EntityState.Modified?Entity Framework Core 5:保存使用 Add(oldItem) 和 db.Entry(oldItem).State = EntityState.Modified 的区别?
【发布时间】:2021-05-23 04:20:48
【问题描述】:

我正在使用 Entity Framework Core 5.0.3、.NET 5.0.103、ASP.NET Core 5 Blazor WebAssebmly。我有 -

// Cập nhật
[HttpPut("{id}")]
public async Task<ActionResult> UpdateItem(int id, ProjectWorkCategory newItem)
{
    var oldItem = await db.ProjectWorkCategories.FindAsync(id);
    if (oldItem == null)
    {
        return NotFound();
    }

    // Mã loại công trình.
    if (!string.IsNullOrEmpty(newItem.ProjectWorkCategoryCode))
    {
        oldItem.ProjectWorkCategoryCode = newItem.ProjectWorkCategoryCode;
    }

    // Tên loại công trình
    if (!string.IsNullOrEmpty(newItem.ProjectWorkCategoryName))
    {
        oldItem.ProjectWorkCategoryName = newItem.ProjectWorkCategoryName;
    }

    // Diễn giải
    if (!string.IsNullOrEmpty(newItem.Description))
    {
        oldItem.Description = newItem.Description;
    }

    // Trạng thái theo dõi.
    if (newItem.ActiveStatus != oldItem.ActiveStatus)
    {
        oldItem.ActiveStatus = newItem.ActiveStatus;
    }

    // Người sửa.
    if (!string.IsNullOrEmpty(newItem.ModifiedBy))
    {
        oldItem.ModifiedBy = newItem.ModifiedBy;
    }
    oldItem.ModifiedDate = DateTime.Now;
    //db.ProjectWorkCategories.Add(oldItem);
    db.Entry(oldItem).State = EntityState.Modified;
    await db.SaveChangesAsync();
    return NoContent();
}

两者有什么区别

db.ProjectWorkCategories.Add(oldItem);
await db.SaveChangesAsync();

db.Entry(oldItem).State = EntityState.Modified;
await db.SaveChangesAsync();

【问题讨论】:

标签: c# asp.net-core entity-framework-core asp.net-core-webapi blazor-webassembly


【解决方案1】:

这个正在添加一个新项目,正如@Navid 所提到的,如果它有一个唯一的 Id,它将通过一个错误。

db.ProjectWorkCategories.Add(oldItem);
await db.SaveChangesAsync();

如果你想更新你应该使用Update 方法:

db.ProjectWorkCategories.Update(oldItem);
await db.SaveChangesAsync();

这也可用于更新现有项目。

db.Entry(oldItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
await db.SaveChangesAsync();

深潜:

更新以解释@atiyar 评论中提到的要点

使用AddUpdate 方法与更改实体状态EntityState.Modified 之间存在一些差异。

当使用(1)Add Update 方法时,如果没有分配Id,框架将开始将该实体跟踪为EntityState.Added ,或者如果它有Id,则将其跟踪为EntityState.Modified

DbContext.Update Method: 对于具有生成键的实体类型,如果一个实体设置了它的主键值,那么它将在 EntityState.Modified 状态下被跟踪。如果未设置主键值,则将在 EntityState.Added 状态下进行跟踪。

(2) 当使用Update 方法时,框架将使用EntityState.Modified 标志跟踪实体及其所有引用的实体。

(2) 我仍然认为这是有效的。请参阅the explanation in this document: 此方法与显式设置 State 属性的不同之处在于,上下文将开始跟踪处于 Modified 状态的任何相关实体(例如本例中的书籍集合),从而导致 UPDATE 语句为他们每个人生成。如果相关实体没有分配键值,则将其标记为已添加,并生成INSERT语句。

当使用EntityState.Modified 时,框架会将这个实体跟踪为EntityState.Modified。但是所有被引用的实体都将被EntityState.Unchanged标志跟踪,您可能需要手动将每个被引用实体的状态更改为Modified

另外一个区别,Update方法之后调用SaveChanges()时,只会将实体的变化值提交给DB。但是当你在使用EntityState.Modified之后调用SaveChanges()时,都是由于所有字段都标记为Modified,因此值将提交给数据库。

(3) 那是我的错误,我曾经使用another library,我们可以使用它只发送修改后的字段进行更新,但这对于EF Update 方法无效。 p>

编辑

参考您的更新方法,您可以同时使用UpdateEntityState.Modified。简单的方法是使用Update(entity)。在某些高级情况下,当实体状态可能为 Detached(未跟踪)时,我使用 EntityState 标志来检查实体是否已附加(跟踪),然后我会相应地修改其状态。

最后,我建议不要避免SaveChangesAsync()的结果,而是检查值并返回:

var success = await db.SaveChangesAsync();

if(success > 0)
    // log or message for success
else
    // log or message for failure

【讨论】:

  • 我的最佳实践是什么(插入、更新)?
  • 我没有投反对票,但这个答案的 Deep dive 部分有几个不正确的信息。 (1) Add 不会将实体跟踪为 Modified,如果它具有 Id,(2) Update 并不总是将 所有 引用的实体跟踪为 Modified,(3) @ 987654361@ 不会导致仅将更改的值提交到数据库
  • @LazZiya 道歉。我忽略了“如果没有分配Id,框架将开始将此实体跟踪为EntityState.Added”部分中的this一词。
  • @atiyar,很高兴我们现在能够相互理解;)
  • @LazZiya 是的。 Point.2 不是关于手头的实体,而是关于相关实体。
【解决方案2】:

实体框架跟踪具有State 值的实体,您可以通过 -

进行检查
var state = db.Entry(myEntity).State;

当您调用 SaveChanges() 时,EF 会尝试根据其 State 值插入、更新或删除实体。

Add()EntityState.Modified 做了什么:

  1. Add()方法将实体标记为Added(将State的值设置为EntityState.Added),实体是否有主键值,以及下次调用SaveChanges()时, EF 将尝试插入该实体。

  2. db.Entry(myEntity).State = EntityState.Modified; 将实体的 State 值显式设置为 Modified,这基本上告诉 EF“此实体已经存在于数据库中,并且已被修改”。因此,在下一次SaveChanges() 调用中,EF 将尝试使用其新值更新实体。

Add()EntityState.Modified 在您的场景中会做什么:

  1. 您正在尝试更新实体以不插入它。对于更新操作,您不应该使用Add() 方法。

  2. 您使用 -

    从数据库中获取了一个现有实体(其中包含其主键值)
var oldItem = await db.ProjectWorkCategories.FindAsync(id);

此时实体将处于Unchanged 状态。然后,一旦您为其任何属性设置新值,实体就会被修改并自动进入Modified 状态。但是如果你打电话 -

db.ProjectWorkCategories.Add(oldItem);

Add() 方法会将实体置于Added 状态。此时调用SaveChanges()会抛出异常,因为EF试图插入一个已经存在于数据库中的实体(主键违规)。

  1. 在您的场景中,通过设置 -
db.Entry(oldItem).State = EntityState.Modified;

您将实体明确标记为Modified,并且在下一次SaveChanges() 调用中,EF 将使用其新值更新实体。但问题是,您甚至不需要上述行。正如我之前提到的,获取现有实体并更改其任何属性值都会自动将实体置于Modified 状态。因此,设置好新值后可以直接调用-

await db.SaveChangesAsync();

您的实体将按预期更新。

【讨论】:

    【解决方案3】:

    当您使用 add 方法时,一条记录将被添加到数据库中。但是如果你使用'State = Microsoft.EntityFrameworkCore.EntityState.Modified',你现有的记录会在数据库中被编辑。

    【讨论】:

      【解决方案4】:
      db.ProjectWorkCategories.Add(oldItem);
      await db.SaveChangesAsync();
      

      上面的代码将实体添加到数据库中。如果'oldItem'有唯一ID,那么你会在尝试保存它时遇到异常,否则它会保存重复记录。

      db.Entry(oldItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
      await db.SaveChangesAsync();
      

      这是更新实体的正确方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-08
        • 1970-01-01
        • 1970-01-01
        • 2013-06-24
        • 2017-05-23
        • 1970-01-01
        • 1970-01-01
        • 2017-04-20
        相关资源
        最近更新 更多