Я хотел бы узнать, как загрузить / получить текущие значения базы данных для объекта («принудительная перезагрузка из базы данных») с помощью 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
На самом деле я предоставляю API, и пользовательский интерфейс извлекает данные из этого API. В моем случае я хочу исправить модель и вернуть «текущие значения». Итак, у меня есть один DbContext для этого запроса, и, насколько мне известно, EF Core загружает данные только один раз для этого объекта (шаблон карты идентификации - stackoverflow.com/a/3653392/732846).
нет, EF Core всегда делает то, что вы ему говорите. Он не кэширует и не «исправляет» сущности, он загружает их каждый раз, когда вы ему указываете. Сущности, которые он загружает каждый раз, - это разные, даже если для их загрузки использовался один и тот же идентификатор.
@PanagiotisKanavos Это определенно неправда. EF Core возвращает тот же экземпляр (с кэшированными данными), если сущность отслеживается, даже если она сначала запрашивает базу данных. В основном клиент выигрывает стратегию.
@IvanStoev наверное - я никогда не использую долгоживущие контексты по таким причинам. И мне пришлось отлаживать довольно много случаев блокировки, тупиковых ситуаций, когда кто-то использовал один контекст и долгоживущих транзакций.
@Edub Reload
у меня работает (EF Core 2.1.1). Какая у вас версия EF Core? И надеюсь, вы позвоните в Reload
после SaveChanges
и без внешней транзакции.
Я тоже использую 2.1.1, и Reload обычно работает. Но в этом конкретном случае с точностью округления и усечения десятичной дроби базой данных / фреймворком Reload (), похоже, не возвращает округленное / усеченное значение базы данных. Возможно, было бы даже лучше проверить модель, прежде чем передавать ее в DataModel, но мне было интересно, что здесь не так.
Просто создайте пустой проект только с одним классом и попытайтесь воспроизвести его. В этом проекте все работает как положено. Пока спасибо - мне нужно воспроизвести это, чтобы дать лучшее описание. Значение комиссии после SaveChanges () 12,3456789 Значение комиссии после Reload () 12,35 Значение комиссии после удаления и воссоздания контекста 12,35
+1 Эта команда ReloadAsync () только что спасла мне жизнь. Я отчаянно пытался заставить EF Core перезагрузить запись из нашей базы данных после того, как API обновил ее, но он продолжал возвращать кешированную версию. Использование ReloadAsync () или установка для dbContext.ChangeTracker.QueryTrackingBehavior значения NoTracking устранили эту проблему для меня. Но мне потребовались ДНИ, чтобы отследить эту проблему.
Иногда AsNoTracking
может быть решением, если вам просто нужно загрузить данные, а не поддерживать весь контекст объектов. https://docs.microsoft.com/en-us/ef/core/querying/tracking
Например, у меня был случай, когда мне нужно было:
Используя AsNoTracking
, я смог дважды загрузить одну и ту же строку и не увидеть ничего кэшированного.
var shipmentData = await context.Shipments.AsNoTracking().Single(s => s.ShipmentId == shipmentId);
ОДНАКО, если вам действительно нужно сохранить эту строку, этот метод может запутать управление или привести к риску потери изменений.
EF Core ничего не кэширует. Вместо того, чтобы пытаться «обновить» тот же объект сущности, просто снова нагрузка данных и привяжите свой пользовательский интерфейс к новым данным.