c# - tutorial - Obtener propiedades de navegación de entidades después de insertar
entity framework visual studio 2017 (4)
Tengo las siguientes 2 clases:
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; }
}
Con esto tengo todas las cosas necesarias obvias como un DbContext y mappings.
Ahora digamos que creo una entidad de recompensa y la inserto así:
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
Obviamente tengo una campaña con Id 1, por lo que la restricción FK es feliz. Después de este inserto, mi entidad de recompensa tiene su nuevo conjunto de Id de identidad. Ahora el problema es que la recompensa sigue siendo solo la entidad de recompensa que creé. Y con esto, la recompensa. La propiedad de Campaña es nula. Parece que EF mantiene las entidades insertadas en la memoria, y cuando hago un .SingleOrDefault (a => a.Id == reward.Id) simplemente devuelve la entidad en la memoria, y no un nuevo proxy. Probablemente esto es algo bueno.
Entonces, la pregunta es: ¿cómo accede uno o carga las propiedades de navegación después de una inserción u obtiene un nuevo proxy que también tenga las propiedades de navegación como proxies?
¿Acaso estoy insertando en el camino equivocado?
¿Intentó usar Include()
? Algo como esto:
reward = context.Set<Reward>().Include("Campaigns").SingleOrDefault(a => a.Id == reward.Id);
A medida que crea su objeto de reward
como new Reward()
, EF no tiene un proxy. En su lugar, créelo usando DbSet.Cree así:
var reward = context.Set<Reward>().Create();
reward.CampaignId = 5;
context.SaveChanges();
A continuación, adjúntelo a su DbSet:
context.Rewards.Attach(reward);
Finalmente, ahora puede usar la carga diferida para obtener entidades relacionadas:
var campaign = reward.Campaign;
Si lo entiendo correctamente, está intentando cargar ansiosamente una propiedad compleja después de establecer una relación a través de una propiedad de clave externa.
SaveChanges()
no hace nada en la forma de cargar propiedades complejas. Como máximo, establecerá la propiedad de la clave principal si agrega nuevos objetos.
Su reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
línea reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
tampoco hace nada en la forma de cargar Campaign
porque su objeto de recompensa no está adjunto al contexto. Necesitas decirle explícitamente a EF que cargue ese objeto complejo o que lo adjunte y que la carga lenta funcione.
Entonces, después de context.SaveChanges();
tienes tres opciones para cargar la reward.Campaign
:
Attach()
recompensa al contexto para queCampaign
se pueda cargar de forma perezosa (se carga cuando se accede)context.Rewards.Attach(reward);
Nota: Solo podrá cargar
reward.Campaign
cargareward.Campaign
en el alcance del contexto para que, si no va a acceder a ninguna propiedad dentro de la duración del contexto, use la opción 2 o 3.Load()
manualmenteLoad()
la propiedadCampaign
context.Entry(reward).Reference(c => c.Campaign).Load();
Include()
manualmenteInclude()
la propiedadCampaign
reward = context.Rewards.Include("Campaigns") .SingleOrDefault(r => r.Id == reward.Id);
Aunque, sugeriría
Load
ya que tienes unareward
en memoria.
Consulte la sección Cargando objetos relacionados en este documento msdn para obtener más información.
Tengo una Solución simple alrededor del problema.
en lugar de agregar el CampaignID a la recompensa, agregue el objeto de la campaña ... entonces:
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
El marco de la entidad hace todo el trabajo pesado aquí.
Probablemente estés pensando que es un desperdicio cargar todo el objeto de Campaign, pero si vas a usarlo (por lo que parece, parece que lo estás), entonces no veo por qué no. Incluso puede usar la instrucción include al recuperarla arriba si necesita acceder a las propiedades de navegación desde el objeto Campaign ...
var _campaign = context.Campaign.include(/*what ever you may require*/).First(c=>c.Id = 1);