web-dev-qa-db-fra.com

Attacher et détacher correctement des entités du contexte dans EF4.1

J'essaie d'implémenter un mécanisme de mise en cache pour les entités. Et pour utiliser les entités correctement et de manière transparente avec la mise en cache, j'ai besoin de détacher l'entité du contexte actuel avant de la mettre dans un cache et de la rattacher au nouveau contexte lorsque je l'obtiens du cache. (Ma durée de vie de contexte est par requête http)

Les exigences sont que -

  1. Toutes les propriétés de navigation qui lui sont associées (que j'ai déjà renseignées) ne doivent pas être supprimées lorsque l'entité est détachée.
  2. Je peux mettre à jour les éléments mis en cache si je le souhaite (il est donc important de les attacher correctement au nouveau contexte).

Ceci est ma tentative de création d'une classe EntityCache - (ServerCache ici est ma classe wrapper qui pousse l'objet dans le cache 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);
            }
        }
    }

Lorsque je mets une entité dans le cache, elle est de type DynamicProxy et PAS la vraie classe.

Attacher ne fonctionne pas du tout - j'obtiens une exception que je ne peux pas caser un objet de type Dynamic_ {blahblah} à IEntityWithKey.

Je viens de voir ces exemples d'attachement et de détachement en ligne et je les ai essayés, je suis ouvert à toute nouvelle implémentation des méthodes d'attachement/détachement ici.

Je vous remercie.

Question de suivi -

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

Fonctionne, mais rend toutes les propriétés de navigation chargées NULL, comment pouvons-nous faire en sorte qu'elles conservent les propriétés de navigation et NE PAS les remplacer (ou les perdre) par NULL lorsque nous nous détachons du contexte.

31
MoXplod

IEntityWithKey est l'interface pour d'autres types d'entités. C'est pour les "grandes" entités. Par exemple, EntityObject implémente cette interface. Ces entités ne sont pas considérées comme POCO et ne sont pas prises en charge par l'API DbContext.

Si vous voulez utiliser IEntityWithKey vos classes doivent l'implémenter - ce n'est pas quelque chose qui se produirait automatiquement.

Une connexion correcte avec l'API DbContext doit être:

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

et cela devrait également fonctionner:

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

Le détachement correct avec l'API DbContext doit être:

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

Aussi, il vaut mieux pour vous des méthodes génériques au lieu de object.

30
Ladislav Mrnka

À votre question de suivi:

... comment pouvons-nous faire en sorte de conserver les propriétés de navigation et de ne PAS les remplacer (ou les perdre) par NULL lorsque nous nous détachons du contexte ...

Je pense qu'il n'est pas possible de détacher un graphique d'objet du contexte tout en conservant ses propriétés de navigation. à partir de MSDN :

Dans une association indépendante, les informations de relation ne sont pas conservées pour un objet détaché.

Bien que cette déclaration concerne les associations indépendantes, elle ne signifie pas que les propriétés de navigation sont conservées dans l'association de clé étrangère (relations qui exposent une propriété de clé étrangère dans le modèle). Oui, les "informations de relation" sont conservées car les propriétés de clé étrangère (qui sont des propriétés scalaires) seront actives et contiendront la valeur de clé étrangère correcte après le détachement. Mais les propriétés de navigation correspondantes seront toujours null pour les propriétés de référence ou, pour les collections de navigation, la référence sera supprimée de la collection.

Je pense que la seule façon de détacher un graphique d'objet complet d'un contexte est de supprimer le contexte ou de créer une copie du graphique avant de commencer à détacher le graphique d'origine. Pour créer une copie, il faudrait écrire des méthodes Clone qui copient toutes les propriétés et naviguer dans le graphique ou utiliser une "astuce" comme this qui sérialise le graphique en un flux binaire puis le désérialise retour à de nouveaux objets. Pour cela, les entités devraient être sérialisables. Les cycles de référence (que nous avons souvent lors de l'utilisation de propriétés de navigation bidirectionnelles entre entités) peuvent également causer des problèmes. (Attention également si vos objets sont des proxys qui contiennent des références aux objets internes d'EF et que vous ne voulez probablement pas copier, sérialiser et désérialiser.)

Dans cet aspect, Detach n'est pas l'équivalent de Attach car il se comporte très différemment: Attach attache le graphique d'objet entier et conserve les propriétés de navigation. Detach ne détache que des entités uniques sans les objets associés et détruit les propriétés de navigation. De la même page liée ci-dessus:

Détacher affecte uniquement l'objet spécifique transmis à la méthode. Si l'objet détaché a des objets associés dans le contexte de l'objet, ces objets ne sont pas détachés.

Je pourrais imaginer que c'est la principale raison pour laquelle l'API DbContext n'a pas de méthode explicite Detach (contrairement à ObjectContext) - le détachement est considéré comme une fonctionnalité avancée qui ne se comporte pas comme on pourrait s'y attendre.

MSDN mentionne comme la seule raison de détacher un objet du contexte "pour conserver les ressources" (encore une fois l'article ci-dessus):

Dans les applications Entity Framework, vous pouvez détacher des objets du contexte d'objet. Vous pouvez détacher des objets pour économiser les ressources, car l'exécution de requêtes répétées dans le même contexte d'objet augmente les besoins en mémoire du contexte d'objet.

Je pense que cette méthode n'est tout simplement pas préparée et conçue pour travailler avec les objets une fois qu'ils ont été détachés du contexte. Son objectif principal est de les libérer pour une collecte immédiate des ordures.

10
Slauma