web-dev-qa-db-fra.com

Obtenir les propriétés de navigation d'entité après l'insertion

J'ai les 2 cours suivants:

public class Reward 
{
    public int Id { get; set; }
    public int CampaignId { get; set;
    public virtual Campaign Campaign { get; set; }
}

public class Campaign 
{
    public int Id { get; set; }
    public virtual ICollection<Reward> Rewards { get; set; }
}

Avec cela, j'ai toutes les choses nécessaires évidentes comme un DbContext et des mappages.

Supposons maintenant que je crée une entité de récompense et l'insère comme ceci:

var reward = new Reward { CampaignId = 1 };
context.Set<Reward>().Add(reward);
context.SaveChanges();

reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
//reward.Campaign is null

J'ai évidemment une campagne avec Id 1 donc la contrainte FK est heureuse. Après cette insertion, mon entité de récompense a son nouvel ensemble d'identités d'identité. Maintenant, le problème est que la récompense n'est toujours que l'entité Récompense que j'ai créée. Et avec cela, la propriété récompense.Campaign est nulle. Il semble que EF conserve les entités insérées en mémoire, et lorsque je fais ensuite un .SingleOrDefault (a => a.Id == récompense.Id), il renvoie simplement l'entité en mémoire, et non un nouveau proxy. C'est probablement une bonne chose.

La question est donc la suivante: comment accéder ou charger les propriétés de navigation après une insertion ou obtenir un nouveau proxy qui a également les propriétés de navigation en tant que proxys.

Suis-je peut-être inséré dans le mauvais sens?

41
Justus Burger

Si je vous comprends bien, vous essayez de charger avec impatience une propriété complexe après avoir établi une relation via une propriété de clé étrangère.

SaveChanges() ne fait rien pour charger des propriétés complexes. Tout au plus, il va définir votre propriété de clé primaire si vous ajoutez de nouveaux objets.

Votre ligne reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id); ne fait rien non plus dans la manière de charger Campaign car votre objet de récompense n'est pas attaché au contexte. Vous devez explicitement dire à EF de charger cet objet complexe ou l'attacher puis laisser le chargement paresseux opérer sa magie.

Ainsi, après avoir context.SaveChanges(); vous avez trois options pour charger reward.Campaign:

  1. Attach() récompense le contexte afin que Campaign puisse être chargé paresseusement (chargé lors de l'accès)

    context.Rewards.Attach(reward);
    

    Remarque: Vous ne pourrez charger paresseusement reward.Campaign Dans la portée du contexte, donc si vous n'accédez à aucune propriété pendant la durée de vie du contexte, utilisez l'option 2 ou 3.

  2. Manuellement Load() la propriété Campaign

    context.Entry(reward).Reference(c => c.Campaign).Load();
    
  3. Manuellement Include() la propriété Campaign

    reward = context.Rewards.Include("Campaigns")
        .SingleOrDefault(r => r.Id == reward.Id);
    

    Cependant, je suggère Load car vous avez déjà reward en mémoire.

Consultez la section Chargement des objets associés sur ce doc msdn pour plus d'informations.

65
Carrie Kendall

Comme vous créez votre objet reward en tant que new Reward(), EF n'a pas de proxy. Au lieu de cela, créez-le à l'aide de DbSet.Créez comme ceci:

var reward = context.Set<Reward>().Create();
reward.CampaignId = 5;
context.SaveChanges();

Attachez-le ensuite à votre DbSet:

context.Rewards.Attach(reward);

Enfin, vous pouvez désormais utiliser le chargement différé pour obtenir des entités liées:

var campaign = reward.Campaign;
9
DavidG

J'ai une solution simple autour du problème.

au lieu d'ajouter le CampaignID à la récompense, ajoutez l'objet de la campagne .. donc:

var _campaign = context.Campaign.First(c=>c.Id == 1);//how ever you get the '1'
var reward = new Reward { Campaign = _campaign };
context.Set<Reward>().Add(reward);
context.SaveChanges();

//reward.Campaign is not null

Le cadre d'entité fait tout le gros du travail ici.

Vous pensez probablement que c'est un gaspillage de charger l'intégralité de l'objet Campaign mais si vous allez l'utiliser (à quoi il ressemble, semble-t-il), alors je ne vois pas pourquoi. Vous pouvez même utiliser l'instruction include lors de la récupération ci-dessus si vous devez accéder aux propriétés de navigation à partir de l'objet Campaign ...

var _campaign = context.Campaign.include(/*what ever you may require*/).First(c=>c.Id = 1);
1
Steven

Avez-vous essayé d'utiliser Include()? Quelque chose comme ça:

reward = context.Set<Reward>().Include("Campaigns").SingleOrDefault(a => a.Id == reward.Id);
1
Floremin

En plus de Carrie Kendall et DavidG (dans VB.NET):

Dim db As New MyEntities
Dim r As Reward = = db.Set(Of Reward)().Create()
r.CampaignId = 5
db.Reward.Add(r) ' Here was my problem, I was not adding to database and child did not load
db.SaveChanges()

Ensuite, la propriété r.Campaign est disponible

0
Dani