Включение и удаление объектов из контекста правильно в EF4.1
Я пытаюсь реализовать механизм кэширования для объектов. И чтобы использовать объекты правильно и плавно с кешированием, мне нужно отделить объект от текущего контекста, прежде чем я поместил его в кеш и приложил его обратно к новому контексту, когда я получу его из кеша. (Мое время жизни зависит от HTTP-запроса)
Требования:
- Все связанные с ним навигационные свойства (которые я уже заполнил) не должны удаляться при отсоединении объекта.
- Я могу обновить кешированные элементы, если захочу (поэтому важно правильно привязать их к новому контексту).
Это моя попытка создания класса EntityCache - (ServerCache - это мой класс-оболочка, который подталкивает объект к кешу ASP.NET)
public static class EntityCache
{
private static DbContext context
{
get
{
return (DbContext)HttpContext.Current.Items[ObjectKeys.ContextRequestItemKey];
}
}
private static void Detach(object entity)
{
var trackedEntity = entity as IEntityWithChangeTracker;
trackedEntity.SetChangeTracker(null);
((IObjectContextAdapter)context).ObjectContext.Detach(entity);
}
private static void Attach(object entity)
{
((IObjectContextAdapter)context).ObjectContext.Attach((IEntityWithKey)entity);
}
public static void Remove(string key)
{
ServerCache.Remove(key);
}
public static object Get(string key)
{
object output = ServerCache.Get(key);
if (output != null)
Attach(output);
return output;
}
public static void ShortCache(String key, object data)
{
if (data != null)
{
Detach(data);
ServerCache.ShortCache(key, data);
}
}
public static void LongCache(String key, object data)
{
if (data != null)
{
Detach(data);
ServerCache.LongCache(key, data);
}
}
}
Когда я помещаю объект в кеш, он имеет тип DynamicProxy и НЕ настоящий класс.
Прикрепление вообще не работает - я получаю исключение, которое не может иметь объект case, который имеет тип Dynamic_ {blahblah} для IEntityWithKey.
Я только что увидел эти примеры подключения и отсоединения в Интернете и попробовал их, я открыт для любой новой реализации методов Attach/Detach здесь.
Спасибо.
Последующий вопрос -
context.Entry(entity).State = EntityState.Detached;
Работает, но делает все навигационные свойства, которые загружаются NULL, как мы это делаем, сохраняем навигационные свойства и НЕ заменяем (или теряем) их с помощью NULL, когда мы отключаемся от контекста.
Ответы
Ответ 1
IEntityWithKey
- это интерфейс для других типов объектов. Это для "больших" сущностей. Например, EntityObject
реализовать этот интерфейс. Эти объекты не считаются POCO и не поддерживаются API DbContext
.
Если вы хотите использовать IEntityWithKey
, ваши классы должны его реализовать - это не то, что произойдет автоматически.
Правильное присоединение с API DbContext
должно быть:
dbContext.Set(typeof(entity)).Attach(entity);
и это, надеюсь, также будет работать:
dbContext.Entry(entity).State = EntityState.Unchanged;
Правильное отключение с помощью DbContext
API должно быть:
dbContext.Entry(entity).State = EntityState.Detached;
Также вам лучше использовать общие методы вместо object
.
Ответ 2
К вашему следующему вопросу:
... как мы можем сохранить навигационные свойства и НЕ замените (или проиграете) их с помощью NULL, когда мы отсоединяемся от контекста...
Я считаю, что невозможно отделить граф объекта от контекста, сохраняя его свойства навигации. Из MSDN:
В независимой ассоциации информация о взаимоотношениях не является поддерживается для отдельного объекта.
Несмотря на то, что это утверждение относится к независимым ассоциациям, это не означает, что свойства навигации сохраняются в связи с внешним ключом (отношения, которые предоставляют свойство внешнего ключа в модели). Да, "Информация о взаимоотношениях" поддерживается, поскольку свойства внешнего ключа (которые являются скалярными свойствами) будут живыми и содержат правильное значение внешнего ключа после отсоединения. Но соответствующие свойства навигации по-прежнему будут null
для ссылочных свойств, или для коллекций навигации ссылка будет удалена из коллекции.
Я думаю, что единственный способ отделить полный граф объекта от контекста - либо полностью избавиться от контекста, либо создать копию графика, прежде чем начать отделять исходный граф. Для создания копии потребуется написать методы Clone
, которые копируют все свойства и перемещаются по графику или используют "трюк", например this, который сериализует граф в двоичный поток а затем десериализует его обратно на новые объекты. Для этого сущности должны быть сериализуемыми. Также могут возникнуть проблемы с эталонными циклами (которые мы часто используем при использовании двунаправленных навигационных свойств между объектами). (Также обратите внимание, если ваши объекты являются прокси-серверами, которые содержат ссылки на внутренние объекты EF и которые вы, вероятно, не хотите копировать, сериализовывать и десериализовать.)
В этом аспекте Detach
не является аналогом Attach
, потому что он ведет себя совершенно по-другому: Attach
присоединяет весь граф объекта и поддерживает свойства навигации. Detach
отделяет только отдельные объекты без связанных объектов и уничтожает свойства навигации. На той же странице, указанной выше:
Отключение влияет только на конкретный объект, переданный методу. Если объект, находящийся в стороне, имеет связанные объекты в контексте объекта, те объекты не отсоединяются.
Я мог представить себе, что это основная причина, по которой API DbContext
не имеет объяснительного метода Detach
(в отличие от ObjectContext
) - отключение рассматривается как дополнительная функция, которая не ведет себя как одна может ожидать.
MSDN упоминает как единственную причину отсоединения объекта от контекста "для сохранения ресурсов" (опять же статья выше):
В приложениях Entity Framework вы можете отсоединять объекты от контекст объекта. Вы можете отсоединять объекты для экономии ресурсов, поскольку выполнение повторяющихся запросов в одном и том же объектном контексте увеличивает требования к памяти контекста объекта.
Я думаю, что этот метод просто не подготовлен и предназначен для работы с объектами после того, как они были отделены от контекста. Основная цель - освободить их для немедленной сборки мусора.