EF Core 更新更新所有列,即使只有一列发生更改

man*_*gia 12 c# entity-framework-core

我对使用 EF Core 还不太熟悉。据我了解,EF Core 有一个“更改跟踪器”,它会查看数据以查看哪些列已被修改,并构建一个仅更新已修改列的 SQL UPDATE 语句,如此处的答案中所述:

EF core 是否比较值以在更新语句中包含列?

然而,这不是我所看到的。例如,我有这个实体:

public class Book
{
    public int BookId { get; set; }
    [StringLength(255)]
    public string Title { get; set; }
    public int AuthorId { get; set; }
    public Author Author { get; set; }
    [StringLength(500)]
    public string Description { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后我使用以下代码更新现有行:

class Program
{
    static void Main(string[] args)
    {
        var context = new TestContext();
        var book = context.Books.Find(1);
        book.Title += " x";
        context.Books.Update(book);
        context.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

我启动了 SQL Profiler 并运行了这段代码。我期望看到一个只更新“标题”列的 UPDATE 语句,但它总是更新每一列:

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Books] SET [AuthorId] = @p0, [Description] = @p1, [Title] = @p2
WHERE [BookId] = @p3;
SELECT @@ROWCOUNT;
',N'@p3 int,@p0 int,@p1 nvarchar(500),@p2 nvarchar(255)',@p3=1,@p0=1,@p1=N'',@p2=N'Some Title x'
Run Code Online (Sandbox Code Playgroud)

例如,Description 列未更改,但它位于 UPDATE 语句中,AuthorId 也是如此。

这是为什么?SET 子句中不应该只包含 Title 吗?

Ger*_*old 19

据我了解,EF Core 有一个“更改跟踪器”

这是正确的,这就是为什么一旦实体附加到上下文,您就可以免于将它们标记为已更新。上线后...

book.Title += " x";
Run Code Online (Sandbox Code Playgroud)

...EF 检测到此更改并将标记标记Title为已修改。无需调用该Update方法。

这就是所谓的“连接”场景。

该Update方法适用于断网场景。此时实体对象未附加到上下文,但其值应保存到数据库中,例如在 Web API 后端中。其基本形式是:

public void SaveBook(Book book)
{
    // Here, 'book' is the book with the changed Title.
    using(var context = new TestContext())
    {
        context.Books.Update(book);
        context.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,EF 无法检测到更改,因为book在进入方法时已经被修改了。你必须告诉 EF 这本书必须更新。但 EF 不知道修改了什么;它能做的最好的事情就是将所有属性(主键除外)标记为已修改。

但是,仍然可以通过将断开连接场景切换为连接场景,使 EF 仅更新实际更新的属性:

public void SaveBook(Book book)
{
    // Here, 'book' is the book with the changed Title.
    using(var context = new TestContext())
    {
        var dbBook = context.Books.Find(book.ID);

        // Copy book's property values to dbBook.
        context.Entry(dbBook).CurrentValues.SetValues(book);

        context.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

可能有充分的理由选择后一种方法而不是前一种方法。


Eri*_*kEJ 3

在您的连接场景中,您不需要调用 Update 方法。