【问题标题】:Entity Framework Attach/Update confusion (EF Core)实体框架附加/更新混淆 (EF Core)
【发布时间】:2020-11-21 18:12:58
【问题描述】:

据我了解,当调用“更新”时,特定实体中的每个属性都会被修改。

另一方面,“附加”方法在“未修改”状态下启动实体。然后,当对特定属性进行操作时,仅修改该特定属性。所以“附加”对于单个属性更改更有用,而“更新”在您要更新实体中的每个属性时更有用(我的理解可能是错误的)。

但是,我不明白的是,在属性更改期间这两种方法都没有被调用时会发生什么。例如,考虑一个名为“students”的表的示例:

student.City = "Calgary";
student.Name = "John Smith";
database.SaveChanges();

由于我们没有将实体中的任何属性标记为已修改,因此上述代码生成的查询有何不同?

【问题讨论】:

  • Attach 确实存在,但 DbContext 上的 Update 方法不存在。您在哪里看到这种方法?
  • 您使用的是什么版本的 EF? EF 核心?
  • _database.students 是复数。这表明它是一个学生的集合,大概是一个 DbSet 。然后你怎么能在一个集合上设置属性 City 或 Name?
  • 当您通过例如_database.students.Find(id) 获取学生时,该学生将附加到上下文中。

标签: c# entity-framework sql-update


【解决方案1】:

考虑以下代码:

students entity = new students() {
    Id = 1,
    City = "New York",
    Name = "Sam"
};
using(SomeContext ctx = new SomeContext())
{
    ctx.Entry(entity).State = EntityState.Modified;
    ctx.SaveChanges();
}

假设我们在数据库中有一条 id = 1 的记录,上面的代码将更新数据库中的那个实体。

Attach 用于当您知道一个实体已经存在于数据库中,但想要进行一些更改,同时在您已经进行更改时将状态更改为已修改。

【讨论】:

  • 我对 efcore 知之甚少,但此答案中的最后一条语句似乎不完整/不完整。 “在 Attach 的情况下,递归分析对象并考虑所有相关实体。在 Entry 的情况下,仅考虑您传递给 Entry 方法的对象,而不考虑其相关实体。”根据 => gavilan.blog/2018/12/09/…
【解决方案2】:

在下面的例子中,实体是通过上下文获取的,所以上下文立即开始跟踪它。当您更改被跟踪实体的属性值时,上下文会将实体的 EntityState 更改为 Modified,并且 ChangeTracker 会记录旧属性值和新属性值。调用 SaveChanges 时,数据库会生成并执行一条 UPDATE 语句。

var author = context.Authors.First(a => a.AuthorId == 1);
author.FirstName = "Bill";
context.SaveChanges();

由于 ChangeTracker 跟踪哪些属性已被修改,上下文将发出一条 SQL 语句,仅更新那些已更改的属性:

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Authors] SET [FirstName] = @p0
WHERE [AuthorId] = @p1;
SELECT @@ROWCOUNT;
',N'@p1 int,@p0 nvarchar(4000)',@p1=1,@p0=N'Bill'

DbContext 更新

DbContext 类提供了用于处理单个或多个实体的 Update 和 UpdateRange 方法。

public void Save(Author author)
{
    context.Update(author);
    context.SaveChanges();
}

与设置实体的状态一样,此方法会导致实体被上下文跟踪为已修改。再一次,上下文无法识别哪些属性值已更改,并且将生成 SQL 以更新所有属性。此方法与显式设置 State 属性的不同之处在于,上下文将开始跟踪处于 Modified 状态的任何相关实体(例如本例中的书籍集合),从而为每个实体生成 UPDATE 语句.如果相关实体没有分配键值,则将其标记为已添加,并生成INSERT语句。

DBContext 附加

当您在实体上使用 Attach 方法时,它的状态将设置为 Unchanged,这将导致根本不会生成任何数据库命令。定义了键值的所有其他可访问实体也将设置为未更改。那些没有键值的将被标记为已添加。但是,既然实体正在由上下文跟踪,您可以通知上下文哪些属性已被修改,以便生成仅更新这些值的正确 SQL:

var context = new TestContext();
var author = new Author {
    AuthorId = 1,
    FirstName = "William",
    LastName = "Shakespeare"
};
author.Books.Add(new Book {BookId = 1, Title = "Othello" });
context.Attach(author);
context.Entry(author).Property("FirstName").IsModified = true;
context.SaveChanges();

以上代码将导致作者实体被标记为已修改,并生成 SQL 以仅更新 FirstName 属性:

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Authors] SET [FirstName] = @p0
WHERE [AuthorId] = @p1;
SELECT @@ROWCOUNT;
',N'@p1 int,@p0 nvarchar(4000)',@p1=1,@p0=N'William'

参考:Read more

【讨论】:

  • 很好解释。特别是。 那些没有键值的将被标记为已添加部分值得一提,因为它可能会让习惯于旧 EF 版本的人感到惊讶。
猜你喜欢
  • 1970-01-01
  • 2014-11-27
  • 1970-01-01
  • 1970-01-01
  • 2011-09-07
  • 2021-12-14
  • 2022-09-23
  • 1970-01-01
  • 2011-06-11
相关资源
最近更新 更多