EF Core Включить все еще ленивая загрузка? - PullRequest
0 голосов
/ 14 марта 2019

В проекте Asp.Net Core 2.2 с ядром EF (последние все, все обновления NuGet были запущены сегодня) у меня есть эта операция:

return Ok(_db.GlobalRoles
             .Include(gr => gr.GlobalRoleFeatures)
                 .ThenInclude(grf => grf.Feature)
             .Include(gr => gr.GlobalRoleCompanyGroupRoles)
                 .ThenInclude(grcgr => grcgr.CompanyGroupRole)
                     .ThenInclude(cgr => cgr.CompanyGroupRoleFeatures)
                         .ThenInclude(cgrf => cgrf.Feature)
             .ToList());

По большей части детали не важны, достаточно сказать, что это дерево сущностей, которое я хочу загрузить. Когда я профилирую БД, это в итоге приводит к 4 запросам. Сначала я обнаружил, что это неожиданно, но проигнорировал это как, возможно, только то, как EF оптимизировал получение этих результатов. Ничего страшного. И полученные данные верны.

Но когда я заверну это в IMemoryCache:

return Ok(_cache.GetOrCreate(nameof(GlobalRole), entry =>
{
    entry.SlidingExpiration = TimeSpan.FromMinutes(_appSettings.DataCacherExpiryMinutes);
    return _db.GlobalRoles
              .Include(gr => gr.GlobalRoleFeatures)
                  .ThenInclude(grf => grf.Feature)
              .Include(gr => gr.GlobalRoleCompanyGroupRoles)
                  .ThenInclude(grcgr => grcgr.CompanyGroupRole)
                      .ThenInclude(cgr => cgr.CompanyGroupRoleFeatures)
                          .ThenInclude(cgrf => cgrf.Feature)
              .ToList();
}));

Хотя первая выборка этих данных работает как положено, последующие выборки из кэша приводят к исключению:

Newtonsoft.Json.JsonSerializationException: Ошибка при получении значения из «GlobalRoleCompanyGroupRoles» для «Castle.Proxies.GlobalRoleProxy». ---> System. Это исключение можно подавить или зарегистрировать, передав идентификатор события «CoreEventId.LazyLoadOnDisposedContextWarning» методу «ConfigureWarnings» в «DbContext.OnConfiguring» или «AddDbContext».

Похоже, что при сериализации объекта нет загруженных списков содержащихся объектов. (Или, возможно, они есть, но он все еще пытается загрузить их снова? Или каким-то образом запросить контекст?) Естественно, экземпляр контекста давно удален, должен быть кэширован только полностью материализованный список.

Когда я отлаживаю, список верхнего уровня действительно возвращается из кеша. Но после проверки свойства GlobalRoleFeatures и GlobalRoleCompanyGroupRoles любого находящегося в нем объекта приводят к тому же вышеупомянутому исключению.

Примечание: То же поведение с использованием .ToListAsync() в запросе и async вплоть до .GetOrCreateAsync() и действия контроллера.

Я что-то пропускаю? Есть ли способ получить полностью материализованный список, больше не зависящий от контекста БД, в кэш-память?

1 Ответ

1 голос
/ 14 марта 2019

Проблема в использовании IMemoryCache. Фактически вы не сериализуете элементы в свой кеш Объекты кэшируются непосредственно в памяти, что означает, что их связи с такими вещами, как DbContext, сохраняются, хотя DbContext нет.

В частности, ленивая загрузка работает так, что EF фактически создает динамический прокси вашего класса сущности и переопределяет (отсюда и необходимость в ключевом слове virtual) свойство ссылки или коллекции с помощью метода получения клиента, который проверяет кэш объекта EF. для элементов, и если они не могут быть найдены, делает запрос, чтобы получить их. Поскольку вы кэшируете непосредственно в памяти, вы кэшируете эти экземпляры прокси-классов, в которых все еще присутствует эта логика.

Это плохая идея использовать IMemoryCache независимо. Вместо этого вы всегда должны использовать IDistributedCache. Существует поставщик MemoryDistributedCache (который на самом деле используется по умолчанию), если вы все еще хотите фактически кэшировать в памяти, но использование IDistributedCache делает для вас две вещи:

  1. Это более универсальный вариант, чем IMemoryCache, поэтому вы можете позже перейти на любого провайдера кэша (Redis, SQL Server и т. Д.) Без изменения кода приложения.

  2. Специально для вашей проблемы здесь заставит фактически сериализовать значение кеша, даже если вы используете провайдер кеша памяти, что означает, что у вас не будет этой же неочевидной проблемы.

Это значит, что это немного больше работы. Вам нужно будет использовать что-то вроде JsonConvert для сериализации и десериализации в / из кэша, но вы можете добавить расширения к IDistributedCache, чтобы позаботиться об этом за вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...