У нас есть странная ошибка, которая возникла в последние пару дней.
В двух словах, Entity Framework, похоже, пытается лениво загрузить коллекцию, которая ранее была загружена (используя Include()
), что приводит к ObjectContextDisposed
Исключению
У нас есть очень простая модель 2 сущностей, которая представляет Retailer и Языки, которые их веб-сайт поддерживает как конфигурацию «многие ко многим».1 сайт розничной торговли может поддерживать множество языков / 1 язык может поддерживаться многими розничными продавцами.
public class Retailer
{
public int Id {get;set;}
public string Name {get; set;}
public string Site {get; set;}
public virtual ICollection<Language> Languages { get; private set; }
//other props omitted for brevity
}
// Reciprocal class exists for Language.
Поскольку эта информация меняется не очень часто, мы кэшируем ее в кеше памяти в нашем WebAPI.
- ApplicationContext загружается в наш DIContainer как
InstancePerRequest
Вызов API запрашивает кэш для всех продавцов
- Если в кеше есть данные, а срок действия кеша не истек, он вернет
List<Retailer>
из кеша - Если в кеше нет данных ИЛИ истечение срока кеша прошло, он запустит следующий запрос, чтобы обновить кеш и вернуть его пользователю.
// Получить всех розничных продавцов, желающих загрузить их контекст подколлекций Языки .Retailers .Include (r => r.Языки) .ToList ();
Теперь мы видим, что через некоторое время вызов API начнет вызывать исключение ( Экземпляр ObjectContext имеетбыл уничтожен ... ) при попытке доступа к языкам розничной торговли.
_cache.GetByKey("retailers").FirstOrDefault().Languages
System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
at System.Data.Entity.Core.Objects.ObjectContext.get_Connection()
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.DataClasses.EntityCollection`1.Load(List`1 collection, MergeOption mergeOption)
at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.DeferredLoad()
at System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.LoadProperty[TItem](TItem propertyValue, String relationshipName, String targetRoleName, Boolean mustBeNull, Object wrapperObject)
at System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.<>c__DisplayClass7`2.<GetInterceptorDelegate>b__1(TProxy proxy, TItem item)
at System.Data.Entity.DynamicProxies.Retailer_AADFD3EC4BC12C1210279CFB5CE3F49612501FB98800B437CE0DA19DFDAF3293.get_Languages()
Кажется, что это происходит совершенно произвольно.Это похоже на то, что какая-то внутренняя часть EntityFramework решает, что коллекция языков Proxy Collection больше не действительна и должна быть перезагружена / загружена заново.
Здесь можно сделать несколько разных вещей:
- Кэширование DTO вместо сущностей EF
- Перебор результатов и использование
dbContext.Entry(entity).State = EntityState.Detached;
для отсоединения каждого - Попробуйте позвонить
AsNoTracking
по нашему запросу
Но мы все довольно опытны с EF здесь и работаем с ним годами, поэтому мы немного озадачены, почему это даже происходит.Существуют ли какие-либо обстоятельства, когда EntityFramework попытается перегрузить коллекцию, которая ранее была загружена с Include()