EF Core принудительно обновить объект из базы данных (принудительное извлечение значений из базы данных)

Я хотел бы узнать, как загрузить / получить текущие значения базы данных для объекта («принудительная перезагрузка из базы данных») с помощью Entity Framework Core. Моя собственность выглядит следующим образом:

[Column(TypeName = "decimal(16, 2)")]
public decimal Fee { get; set; }

Если комиссия сохраняется с более высокой точностью, чем та, которая указана в атрибуте столбца, например. 1,2888 База данных округляет ее до двух знаков после запятой, но сохраняемая мной сущность не обновляется.

Поэтому я попытался перезагрузить значения из базы данных, чтобы показать «правильные текущие» значения в этом пользовательском интерфейсе, но ни одно из следующих действий не помогло:

// removed from tracked entities and fetch from db
dbContext.Entry(entity).State = EntityState.Detached;
dbContext.Find(...);

// call reload
dbContext.Entry(entity).Reload();

Ожидается, что после обновления / перезагрузки значение будет 1,29, но оно всегда остается 1,2888. Я нашел значение в базе данных, и это был 1,29, а также следующий запрос вернул бы 1,29, но мне не удалось вернуть правильное значение в том же запросе.

Есть ли способ принудительно обновить сущность из базы данных?

--- Редактировать ---

Проблема заключалась в том, что у меня была сущность со свойствами навигации, а десятичное число было в свойстве навигации, которое не было перезагружено при вызове .Reload () для самой сущности.

Код

using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace WebApplication1
{
    public class Entity
    {
        public long Id { get; set; }

        public Fee Fee { get; set; }
    }

    public class Fee
    {
        public long Id { get; set; }

        [Column(TypeName = "decimal(16, 2)")]
        public decimal Value { get; set; }
    }


    public class MyContext : DbContext
    {
        public DbSet<Entity> Entities { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"connectionString");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
        }
    }

    public class Program
    {
        public static void Main()
        {
            AsyncMethod().GetAwaiter().GetResult();
        }

        private static async Task AsyncMethod()
        {
            using (var context = new MyContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();
            }

            using (var context = new MyContext())
            {
                var entity = new Entity {Fee = new Fee {Value = 12.3456789m}};
                context.Add(entity);
                await context.SaveChangesAsync();
                Console.WriteLine($"Fee value after SaveChanges() {entity.Fee.Value}");
                await context.Entry(entity).ReloadAsync();
                Console.WriteLine($"Fee value after Reload() {entity.Fee.Value}");
            }

            using (var context = new MyContext())
            {
                var entity = await context.Entities.OrderByDescending(x => x.Id).Include(x => x.Fee).FirstAsync();
                Console.WriteLine($"Fee value after Disposing and Recreating Context {entity.Fee.Value}");
            }

        }
    }
}

Выход:

Fee value after SaveChanges() 12,3456789
Fee value after Reload() 12,3456789
Fee value after Disposing and Recreating Context 12,35

EF Core ничего не кэширует. Вместо того, чтобы пытаться «обновить» тот же объект сущности, просто снова нагрузка данных и привяжите свой пользовательский интерфейс к новым данным.

Panagiotis Kanavos 10.08.2018 13:27

На самом деле я предоставляю API, и пользовательский интерфейс извлекает данные из этого API. В моем случае я хочу исправить модель и вернуть «текущие значения». Итак, у меня есть один DbContext для этого запроса, и, насколько мне известно, EF Core загружает данные только один раз для этого объекта (шаблон карты идентификации - stackoverflow.com/a/3653392/732846).

Edub 10.08.2018 13:48

нет, EF Core всегда делает то, что вы ему говорите. Он не кэширует и не «исправляет» сущности, он загружает их каждый раз, когда вы ему указываете. Сущности, которые он загружает каждый раз, - это разные, даже если для их загрузки использовался один и тот же идентификатор.

Panagiotis Kanavos 10.08.2018 13:53

@PanagiotisKanavos Это определенно неправда. EF Core возвращает тот же экземпляр (с кэшированными данными), если сущность отслеживается, даже если она сначала запрашивает базу данных. В основном клиент выигрывает стратегию.

Ivan Stoev 10.08.2018 13:58

@IvanStoev наверное - я никогда не использую долгоживущие контексты по таким причинам. И мне пришлось отлаживать довольно много случаев блокировки, тупиковых ситуаций, когда кто-то использовал один контекст и долгоживущих транзакций.

Panagiotis Kanavos 10.08.2018 14:02

@Edub Reload у меня работает (EF Core 2.1.1). Какая у вас версия EF Core? И надеюсь, вы позвоните в Reload после SaveChanges и без внешней транзакции.

Ivan Stoev 10.08.2018 14:07

Я тоже использую 2.1.1, и Reload обычно работает. Но в этом конкретном случае с точностью округления и усечения десятичной дроби базой данных / фреймворком Reload (), похоже, не возвращает округленное / усеченное значение базы данных. Возможно, было бы даже лучше проверить модель, прежде чем передавать ее в DataModel, но мне было интересно, что здесь не так.

Edub 10.08.2018 14:13

Просто создайте пустой проект только с одним классом и попытайтесь воспроизвести его. В этом проекте все работает как положено. Пока спасибо - мне нужно воспроизвести это, чтобы дать лучшее описание. Значение комиссии после SaveChanges () 12,3456789 Значение комиссии после Reload () 12,35 Значение комиссии после удаления и воссоздания контекста 12,35

Edub 10.08.2018 14:35

+1 Эта команда ReloadAsync () только что спасла мне жизнь. Я отчаянно пытался заставить EF Core перезагрузить запись из нашей базы данных после того, как API обновил ее, но он продолжал возвращать кешированную версию. Использование ReloadAsync () или установка для dbContext.ChangeTracker.QueryTrackingBehavior значения NoTracking устранили эту проблему для меня. Но мне потребовались ДНИ, чтобы отследить эту проблему.

Mike Gledhill 01.11.2018 10:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
9
3 764
1

Ответы 1

Возможное решение в ограниченных ситуациях

Иногда AsNoTracking может быть решением, если вам просто нужно загрузить данные, а не поддерживать весь контекст объектов. https://docs.microsoft.com/en-us/ef/core/querying/tracking

Например, у меня был случай, когда мне нужно было:

  • Загрузить одну строку
  • Запустить задачу зависания (выполняется в собственном контексте)
  • Проверьте статус через несколько секунд в исходном методе

Используя AsNoTracking, я смог дважды загрузить одну и ту же строку и не увидеть ничего кэшированного.

var shipmentData = await context.Shipments.AsNoTracking().Single(s => s.ShipmentId == shipmentId);

ОДНАКО, если вам действительно нужно сохранить эту строку, этот метод может запутать управление или привести к риску потери изменений.

Другие вопросы по теме