Хранимая процедура EF Core FromSqlRaw не дает обновленных значений

При запуске процедуры в EF Core 3 с использованием FromSqlRaw, который обновляет значения в таблице, EF НЕ возвращает обновленные значения, когда я запрашиваю эти измененные значения в базе данных.

Мне удалось воспроизвести это поведение. Чтобы воспроизвести, создайте новое консольное приложение C # с .net core 3.1.

Скопируйте и вставьте приведенный ниже код в основной файл Program.cs:

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");
        }
    }
}

Убедитесь, что установлены следующие пакеты:

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design

При необходимости измените строку подключения.

Запустите dotnet ef migrations add initial

Запустите dotnet ef database update

Запустите следующий код в своей базе данных:

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');

Поэтому, когда вы запускаете основную программу при отладке и устанавливаете прерыватель, вы заметите, что НИ ОДИН из объектов не возвращает значений, которые были обновлены процедурой при переходе к ее проверке. Во время отладки, если вы запустите оператор select для таблицы, вы увидите, что поле «Комментарий» действительно обновлено.

Почему это?


person Paul Carlton    schedule 06.02.2020    source источник
comment
stackoverflow.com/ questions / 46205114 /   -  person lptr    schedule 06.02.2020


Ответы (1)


Это относится не только к FromSql, а к способу отслеживания EF Core (все версии) запросы работают.

Вот выдержка из EF Core Как работают запросы тема документации:

Ниже приводится общий обзор процесса, через который проходит каждый запрос.

  1. 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
  2. The result is passed to the database provider
    • The database provider identifies which parts of the query can be evaluated in the database
    • Эти части запроса переводятся на язык запросов к базе данных (например, SQL для реляционной базы данных).
    • Один или несколько запросов отправляются в базу данных, и возвращается набор результатов (результаты - это значения из базы данных, а не экземпляры сущностей)
  3. 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 - это действительно не имеет значения, потому что это применяется ко всему запросу) или просто

dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

и теперь вы увидите обновленные значения (из вашего вызова SP или из других сеансов в той же базе данных).

person Ivan Stoev    schedule 06.02.2020
comment
Хорошо, поэтому я не знал об отсутствии отслеживания, и, по-новому, это как-то связано с тем, как ef кэширует результаты. Я использовал AsNoTracking в неправильном запросе, и именно тогда я решил задать вопрос. Спасибо за ваш ответ и за выдержку высокого уровня. Мои тесты для моей реализации сейчас проходят. Однако это вызывает у меня вопрос дизайна: должны ли AsNoTracking для большинства запросов к базе данных, возвращающих результаты из репозиториев, быть стандартными в контексте моего проекта. - person Paul Carlton; 07.02.2020
comment
Это также было ответом на мои проблемы, несмотря на то, что я не вносил никаких изменений, результаты моей хранимой процедуры, которую я выполнял, не обновлялись внутри цикла, пока я не добавил .AsNoTracking (). ToList () - person CodeThief; 22.09.2020