Pourquoi mon code Entity Framework Première null collection proxy et pourquoi je ne peux pas le mettre?

StackOverflow https://stackoverflow.com/questions/4069563

Question

J'utilise DBContext et 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 je fais une recherche du contexte. Cependant, une propriété de collection est encore nulle lorsque je tente d'ajouter. Je pensais que la procuration assurerait que la collecte est initialisé.

Parce que mon objet Poco peut être utilisé en dehors de son contexte de données, j'ai ajouté un chèque de la collection étant nulle dans le constructeur et créer, si nécessaire:

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

qui fonctionne en dehors du contexte de données, mais si je récupérer un objet à l'aide d'une requête, bien que le test est vrai, lorsque je tente de le régler, je me exception suivante: « La propriété « DanceEvents » sur le type « DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94 » ne peut pas être ensemble parce que la collection est déjà sur un EntityCollection.

Je peux le voir est nul et je ne peux pas ajouter, mais je ne puis le mettre à 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 applications pour ces classes dans la classe de contexte.

Était-ce utile?

La solution 2

J'ai trouvé la solution à ce problème: code ajoutant d'abord aux collections? Comment utiliser le code d'abord avec les dépôts?

Je retire « virtuel » de toutes les propriétés, à l'exception des collections et des objets chargés paresseux, qui est, tous les types natifs.

Mais je ne comprends toujours pas comment vous pouvez vous retrouver avec la situation dans laquelle vous avez une collection null que vous ne pouvez pas utiliser et n'a aucun moyen de le mettre à une collection valide.

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

  

Salut,

     

Si vous faites vos propriétés virtuelles, puis EF va générer des classes proxy à l'exécution qui découle de votre POCO classés, ces proxies permettent EF de se renseigner sur les changements en temps réel plutôt que d'avoir à saisir les valeurs d'origine de votre objet, puis rechercher les modifications lorsque vous enregistrez (ce qui est évidemment présente des avantages de performance 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). Ceux-ci sont connus comme des « proxies de suivi du changement », si vous faites vos propriétés de navigation virtuelle alors un proxy est toujours généré, mais il est beaucoup plus simple et utilise seulement une certaine logique pour effectuer le chargement paresseux lorsque vous accédez à une propriété de navigation.

     

Parce que votre code d'origine générait des procurations de suivi des changements, EF a été le remplacement de votre propriété de collection avec un type de collection spéciale pour l'aider à découvrir des changements. Parce que vous essayez de définir l'arrière de collection à une simple liste dans le constructeur que vous obtenez l'exception.

     

Sauf si vous voyez des problèmes de performance que je suivrais la suggestion de Terrence et simplement supprimer « virtuel » de vos propriétés non-navigation.

     

~ Rowan

Il semble donc que je n'ai que le problème avec un « proxy de suivi du changement » complet si toutes mes propriétés sont virtuelles. Mais étant donné que, pourquoi je ne peux toujours pas utiliser la propriété virtuelle sur le proxy de suivi des modifications? Ce code explose en ligne trois parce que ds2.DanceEvents est nul et ne peuvent pas être définies dans le constructeur:

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

Je ne comprends toujours pas, même si mon code fonctionne maintenant à cause de la solution ci-dessus.

Autres conseils

Comme vous avez pu observer correctement la réponse à votre propre question, en supprimant le mot-clé « virtuel » à partir des propriétés de collecte des œuvres autour du problème, en empêchant le Entity Framework de créer un proxy de suivi des modifications. Cependant, ce n'est pas une solution pour beaucoup de gens, parce que les procurations de suivi des changements peuvent être très pratique et peut aider à prévenir des problèmes lorsque vous oubliez de détecter les changements aux bons endroits dans votre code.

Une meilleure approche serait de modifier vos classes POCO, afin qu'ils instancier les propriétés de collecte dans leur accesseur, plutôt que dans le constructeur. Voici votre classe poco, modifié pour permettre le suivi des modifications création proxy:

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 est plus automatique, mais a plutôt un champ de support. Il est préférable si vous laissez le poseur protégé, empêchant 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ée.

question Old ...

class Poco:

public partial class MyPOCO
{
    public MyPOCO()
    {
        this.MyPocoSub = new HashSet<MyPocoSub>();
    }

    //VIRTUAL
    public virtual ICollection<MyPocoSub> MyPocoSub { get; set; }
}

et le code proxy:

    public override ICollection<MyPocoSubSet> MyPocoSubSets
    {
        get
        {
            ICollection<MyPocoSubSet> myPocoSubSets = base.MyPocoSubSets;
            if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets))
            {
                return base.MyPocoSubSets;
            }
            return myPocoSubSets;
        }
        set
        {
            if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source"))
            {
                // EXCEPTION 
                throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection.");
            }
            base.MyPocoSubSets = value;
        }
    }

Comme vous pouvez le voir cette exception soulevée dans la classe proxy dans ExtityFramework 5. Cela signifie que le comportement existent encore.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top