关于sql server:EF核心存储过程FromSqlRaw不提供更新的值

EF Core Stored Procedure FromSqlRaw Does Not Give Updated Values

当使用FromSqlRaw在EF Core 3上运行过程以更新表中的值时,当我查询数据库中那些更改后的值时,EF不返回更新后的值。

我已经能够重现此行为。 若要重现,请使用.net core 3.1创建一个新的控制台应用程序c#。

将下面的代码复制粘贴到您的主Program.cs文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Linq;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;

namespace EfCoreTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // testing proc
            var dbContext = new TestContext();
            var tables = dbContext.TestTables.ToList();
            var updated = dbContext.TestTables
                .FromSqlRaw("execute testProc @Id=@Id, @Comments=@Comments", new object[]
                {
                    new SqlParameter("Id", 1),
                    new SqlParameter("Comments","testing comments 2"),
                })
                .ToList();
            var again = dbContext.TestTables.ToList();
        }
    }


    public class TestTable
    {
        public int TestTableId { get; set; }

        public string Comment { get; set; }
    }

    public class TestContext : DbContext
    {
        public DbSet<TestTable> TestTables { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=localhost\\SQLEXPRESS;Database=TestDb;Trusted_Connection=True");
        }
    }
}

确保已安装以下软件包:

1
2
3
4
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design

如有必要,请更改您的连接字符串。

运行dotnet ef migrations add initial

运行dotnet ef database update

在您的数据库中运行以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
drop procedure if exists testProc
go
create procedure testProc
@Id int,
@Comments nvarchar(max)
as
begin
    update dbo.TestTables
    set Comment = @Comments
    where TestTableId = @Id;

    select * from dbo.TestTables;
end

go


INSERT INTO [dbo].[TestTables]
(Comment) VALUES ('Test Comment');

因此,当您在调试时运行Main程序并放置断路器时,您会注意到没有一个对象返回检查过程中该过程更新的值。 在调试期间,如果在表上运行select语句,则会看到" Comment"字段确实已更新。

为什么是这样?


这不是特定于FromSql,而是EF Core(所有版本)跟踪查询的工作方式。

以下是EF Core How Queries Work文档主题的摘录:

The following is a high level overview of the process each query goes through.

  • The LINQ query is processed by Entity Framework Core to build a representation that is ready to be processed by the database provider

    • The result is cached so that this processing does not need to be done every time the query is executed
  • The result is passed to the database provider

    • The database provider identifies which parts of the query can be evaluated in the database
    • These parts of the query are translated to database specific query language (for example, SQL for a relational database)
    • One or more queries are sent to the database and the result set returned (results are values from the database, not entity instances)
  • For each item in the result set

    • If this is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance
      If so, the existing entity is returned
      If not, a new entity is created, change tracking is setup, and the new entity is returned
  • 注意最后一个项目符号。 他们所做的基本上是所谓的"客户获胜"策略的实现(与您正在寻找的数据库获胜相对),当前除了使用无跟踪查询外,没有其他方法可以更改。

    在您的示例中,将AsNotTracking()插入查询中的某个位置(在ToList之前,dbContext.TestTables之后–但这并不重要,因为它适用于整个查询),或者

    1
    dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

    现在,您将看到更新后的值(从您的SP调用或从其他会话到同一数据库)。