Поймал багу с Entity Framework Core 2.2 (Npgsql) и не могу понять, что происходит за кулисами, в чем и прошу помочь разобраться.
Рассмотрим для примера код:
using (var scope1 = Services.CreateScope())
{
var db1 = scope1.ServiceProvider.GetRequiredService<DbContext>();
var item1 = await db1.Items.FirstOrDefaultAsync();
//Executed DbCommand: SELECT a."Id", a."Value" FROM "Items" AS a LIMIT 1
item1.Value = "123";
await db1.SaveChangesAsync();
//Executed DbCommand: UPDATE "Items" SET "Value" = @p0 WHERE "Id" = @p1;
// Представим, что этот using исполняется на другой машине
using (var scope2 = Services.CreateScope())
{
var db2 = scope2.ServiceProvider.GetRequiredService<DbContext>();
var item2 = await db2.Items.FirstOrDefaultAsync();
//Executed DbCommand: SELECT a."Id", a."Value" FROM "Items" AS a LIMIT 1
item2.Value = "qwe";
await db2.SaveChangesAsync();
//Executed DbCommand: UPDATE "Items" SET "Value" = @p0 WHERE "Id" = @p1;
}
item1 = await db1.Items.FirstOrDefaultAsync();
//Executed DbCommand: SELECT a."Id", a."Value" FROM "Items" AS a LIMIT 1
//Result: item1.Value = "123" - old value... =(
}
Проблема в том, что db1
не видит изменения, которые были внесены через db2
. Т.е. в консоли отладки я вижу, что Executed: SELECT ... FROM "Items" AS a LIMIT 1
, но полученное значение item1
содержит старое значение "123"
, вместо "qwe"
.
Кстати,
scope
, то все нормально..AsNoTracking()
, то все нормально.Это очень похоже на то, что данные просто закешировались, но в консоли то выводится, что запросы к базе таки идут...
В результате работы данного кода, в БД физически отправляется пять SQL запросов (в том числе, последний SELECT
). Так вот, что это за кеширование такое, при котором SELECT
запрос отправляется, но его результат игнорируется? В моем понимании кеширование - это когда мы "помним" данные, чтобы НЕ делать лишних запросов. А тут наоборот. Должно же быть этому объяснение...
EF реализует паттерн UnitOfWork: ваш DbContext содержит набор репозиториев, вы можете к ним обращаться, чтобы читать и писать данные.
При этом EF "помнит" в каком состоянии находится база данных. Вы можете изменять данные в оперативной памяти, но реальное сохранение в базу данных происходит только тогда, когда вы запустите .SaveChanges()
.
Это хранение состояния происходит на уровне экземпляра DbContext. Поэтому когда вы принудительно создали два разных экземпляра — то и изменения в них трекаются независимо.
Поэтому никакой ошибки Entity Framework в вашем коде нет, просто вы не в курсе, как это должно работать.
Если вы хотите отслеживание изменений — используйте один экземпляр. Приведённый вами пример является концептом для демонстрации, я думаю, что в вашем реальном коде нет никакой необходимости открывать второй scope, раз можно обойтись одним - либо не закрывая вовсе using, либо закрыть и тут же открыть новый. На практике такой пример как у вас стараются не допускать.
Вторая часть вашего вопроса о AsNoTracking. Дело в том, что указывая AsNoTracking вы (в качестве побочного эффекта, основное-то — не трекать изменения) заставляете взять не значение из "кеша", а прочитать из базы. (Не буду утверждать, что правильно понимаю механику, но мне кажется дело происходит примерно так: у вас есть как вы говорите "в кеше" одна запись, которая трекает изменения, когда вы просите создать нетреканную - она не берётся как копия уже имеющейся, а лезет в базу.)
В реальном коде я бы не стал полагаться на такую механику, потому что это нетривиально (может поломаться), да и предполагает, что программист хорошо понимает, что происходит под капотом. (иначе для того, кто читает этот код (а это можете быть и вы: через полгода уже не вспомните, что и зачем) будет казаться "магией").
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Собственно при таком раскладе происходит эта ошибка (смвложения) два варианта обращения к процедуре, с разным аргументом, необходимы по причине...
Всем доброго времени суток! Прошу помочь разобраться в том, как работает GroupBy и в чем разница приведенного ниже кода
Есть мое приложениекоторое работает, и вот в него добавлю механизм записи технической информации