Pourquoi ma collection proxy de code Entity Framework First est-elle nulle et pourquoi ne puis-je pas la définir?

collections entity-framework poco proxies

Question

J'utilise DBContext et j'ai deux classes dont les propriétés sont toutes virtuelles. Je peux voir dans le débogueur que je reçois un objet proxy lorsque j'interroge le contexte. Cependant, une propriété de collection est toujours nulle lorsque j'essaie de l'ajouter. Je pensais que le proxy assurerait l'initialisation de la collection.

Parce que mon objet Poco peut être utilisé en dehors de son contexte de données, j'ai ajouté une vérification de la collection étant null dans le constructeur et le crée si nécessaire:

public class DanceStyle
{
    public DanceStyle()
    {
        if (DanceEvents == null)
        {
            DanceEvents = new Collection<DanceEvent>();
        }
    }
    ...
    public virtual ICollection<DanceEvent> DanceEvents { get; set; }
}

Cela fonctionne en dehors du contexte de données, mais si je récupère un objet à l'aide d'une requête, bien que le test soit vrai, lorsque j'essaie de le définir, j'obtiens l'exception suivante: 'La propriété' DanceEvents 'sur le type' DanceStyle_B6089AE40D178593955F1328A70D8F0D0F9F9D9F9D9F9D9F9A0319A03A03A03. collection est déjà définie sur EntityCollection. '

Je peux voir qu'il est nul et je ne peux pas en ajouter, mais je ne peux pas non plus le définir sur une collection car le proxy dit qu'il est déjà défini. Par conséquent, je ne peux pas l'utiliser. Je suis confus.

Voici la classe DanceEvent:

public class DanceEvent
{
    public DanceEvent()
    {
        if (DanceStyles == null)
        {
            DanceStyles = new Collection<DanceStyle>();
        }
    }
    ...
    public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}

J'ai omis les autres propriétés de type valeur du code ci-dessus. Je n'ai pas d'autres mappages pour ces classes dans la classe de contexte.

Réponse acceptée

J'ai trouvé la solution à ce problème ici: Code Tout d'abord ajouter aux collections? Comment utiliser Code First avec des référentiels?

J'ai supprimé "virtuel" de toutes les propriétés, à l'exception des collections et des objets chargés paresseux, c'est-à-dire de tous les types natifs.

Mais je ne comprends toujours pas comment vous pouvez vous retrouver dans la situation où vous avez une collection null que vous ne pouvez pas utiliser et que vous n'avez aucun moyen de la définir comme collection valide.

J'ai également trouvé cette réponse de Rowan Miller sur un forum MSDN

Salut,

Si vous rendez toutes vos propriétés virtuelles, EF générera au moment de l'exécution des classes de proxy dérivées de votre classe POCO. Ces mandataires permettent à EF de connaître les modifications en temps réel plutôt que de capturer les valeurs d'origine de votre objet, puis de rechercher les modifications. lorsque vous enregistrez (cela présente évidemment des avantages en termes de performances et d’utilisation de la mémoire, mais la différence sera négligeable sauf si vous avez un grand nombre d’entités chargées en mémoire). Celles-ci sont appelées «proxy de suivi des modifications». Si vous rendez vos propriétés de navigation virtuelles, un proxy est toujours généré, mais il est beaucoup plus simple et inclut simplement une logique permettant d'effectuer un chargement paresseux lorsque vous accédez à une propriété de navigation.

Étant donné que votre code d'origine générait des proxy de suivi des modifications, EF remplaçait votre propriété de collection par un type de collection spécial pour l'aider à découvrir les modifications. Parce que vous essayez de redéfinir la collection sur une simple liste dans le constructeur, vous obtenez l'exception.

À moins que vous ne voyiez des problèmes de performances, je suivrais la suggestion de Terrence et supprimerais simplement «virtuel» de vos propriétés autres que de navigation.

~ Rowan

Il semble donc que le problème avec un «proxy de suivi des modifications» complet ne se pose que si toutes mes propriétés sont virtuelles. Mais étant donné cela, pourquoi ne puis-je toujours pas utiliser la propriété virtuelle sur le proxy de suivi des modifications? Ce code explose sur la ligne trois car ds2.DanceEvents est null et ne peut pas être défini dans le constructeur:

DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);

Je suis toujours confus, même si mon code fonctionne maintenant à cause du correctif ci-dessus.


Réponse populaire

Comme vous l'avez correctement observé dans la réponse à votre propre question, la suppression du mot clé "virtuel" des propriétés de la collection permet de contourner le problème, en empêchant Entity Framework de créer un proxy de suivi des modifications. Cependant, ce n'est pas une solution pour beaucoup de gens, car les mandataires de suivi des modifications peuvent être très pratiques et permettent d'éviter des problèmes lorsque vous oubliez de détecter les modifications aux bons endroits de votre code.

Une meilleure approche consisterait à modifier vos classes POCO afin qu’elles instancient les propriétés de la collection dans leur accesseur get plutôt que dans le constructeur. Voici votre classe POCO, modifiée pour permettre la création de proxy de suivi des modifications:

public class DanceEvent
{
    private ICollection<DanceStyle> _danceStyles;
    public virtual ICollection<DanceStyle> DanceStyles
    {
        get { return _danceStyles ?? (_danceStyles = new Collection<DanceStyle>()); }
        protected set { _danceStyles = value; }
    }
}

Dans le code ci-dessus, la propriété de collection n'est plus automatique, mais possède plutôt un champ de sauvegarde. Il est préférable de laisser le sélecteur protégé, afin d'empêcher tout code (autre que le proxy) de modifier ultérieurement ces propriétés. Vous remarquerez que le constructeur n'était plus nécessaire et a été supprimé.



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow