Присоединение и отсоединение сущностей от контекста правильно в EF4.1 - PullRequest
31 голосов
/ 06 октября 2011

Я пытаюсь реализовать механизм кэширования для сущностей. И чтобы правильно и без проблем использовать сущности с кэшированием, мне нужно отсоединить сущность от текущего контекста, прежде чем я помещу ее в кеш и присоединю обратно в новый контекст, когда получу ее из кеша. (Время жизни моего контекста соответствует запросу http)

Требования следующие -

  1. Все навигационные свойства, связанные с ним (которые я уже заполнил), не должны удаляться при отсоединении объекта.
  2. Я могу обновить кэшированные элементы, если захочу (поэтому важно правильно прикрепить их к новому контексту).

Это моя попытка создания класса 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, а НЕ реальный класс.

Присоединение вообще не работает - я получаю исключение, что я не могу найти объект типа Dynamic_ {blahblah} в IEntityWithKey.

Я только что видел эти примеры присоединения и отсоединения онлайн и попробовал их. Я открыт для любой новой реализации методов Присоединения / Отсоединения здесь.

Спасибо.

Следующий вопрос -

context.Entry(entity).State = EntityState.Detached;

Работает, но делает все навигационные свойства, которые загружены, NULL, как нам сделать так, чтобы они сохраняли навигационные свойства и НЕ заменяли (или теряли) их на NULL, когда мы отсоединяемся от контекста.

Ответы [ 2 ]

29 голосов
/ 07 октября 2011

IEntityWithKey - интерфейс для других типов объектов.Это для "больших" юридических лиц.Например EntityObject реализовать этот интерфейс.Эти объекты не рассматриваются как POCO и не поддерживаются DbContext API.

Если вы хотите использовать IEntityWithKey, ваши классы должны реализовать это - это не то, что происходит автоматически.

Правильное присоединение с помощью DbContext API должно быть:

dbContext.Set(typeof(entity)).Attach(entity); 

, и, надеюсь, это также будет работать:

dbContext.Entry(entity).State = EntityState.Unchanged;

Правильное отсоединение с DbContext API должно быть:

dbContext.Entry(entity).State = EntityState.Detached;

Также вам лучше родовые методы вместо object.

10 голосов
/ 08 октября 2011

К вашему последующему вопросу:

... как мы можем сохранить навигационные свойства, а НЕ замените (или потеряете) их на NULL, когда мы отсоединяемся от контекста ...

Я считаю, что невозможно отделить граф объекта от контекста, сохраняя его свойства навигации. Из MSDN :

В независимой ассоциации информация об отношениях не является поддерживается для отдельного объекта.

Хотя это утверждение относится к независимым ассоциациям, это не означает, что свойства навигации поддерживаются в ассоциации внешнего ключа (отношения, которые предоставляют свойство внешнего ключа в модели). Да, «информация об отношениях» сохраняется, потому что свойства внешнего ключа (которые являются скалярными свойствами) будут действующими и будут содержать правильное значение внешнего ключа после отсоединения. Но соответствующие свойства навигации по-прежнему будут null для ссылочных свойств или, для навигационных коллекций ссылка будет удалена из коллекции.

Я думаю, что единственный способ отсоединить полный граф объектов от контекста - это либо полностью удалить контекст, либо создать копию графа перед началом отсоединения исходного графа. Создание копии потребует написания Clone методов, которые копируют все свойства и перемещаются по графику, или используют «трюк», такой как this , который сериализует график в двоичный поток и затем десериализует его обратно в новые объекты. , Для этого сущности должны быть сериализуемыми. Также ссылочные циклы (которые мы часто имеем при использовании свойств двунаправленной навигации между объектами) могут вызвать проблемы. (Также обратите внимание, если ваши объекты являются прокси-серверами, которые содержат ссылки на внутренние объекты EF и которые вы, вероятно, не хотите копировать, сериализовать и десериализовать.)

В этом аспекте Detach не является аналогом Attach, поскольку он ведет себя совершенно иначе: Attach присоединяет весь граф объекта и поддерживает свойства навигации. Detach отсоединяет только отдельные объекты без связанных объектов и уничтожает свойства навигации. С той же страницы, на которую ссылаются выше:

Отсоединение влияет только на конкретный объект, переданный методу. Если отсоединяемый объект имеет связанные объекты в контексте объекта, те объекты не отделены.

Я мог бы представить, что это главная причина, по которой API DbContext не имеет явного Detach метода (в отличие от ObjectContext) - отсоединение рассматривается как расширенная функция, которая не ведет себя как единое целое. можно ожидать.

MSDN упоминает в качестве единственной причины отсоединения объекта от контекста "для сохранения ресурсов" (снова статья выше):

В приложениях Entity Framework вы можете отсоединять объекты от контекст объекта. Вы можете отсоединить объекты для сохранения ресурсов, как выполнение повторных запросов в одном и том же контексте объекта увеличивает Требования к памяти для контекста объекта.

Я думаю, что этот метод просто не подготовлен и предназначен для работы с объектами после того, как они были отделены от контекста. Его главная цель - освободить их для немедленной сборки мусора.

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