Получить свойства навигации сущности после вставки

У меня есть следующие 2 класса:

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; }
}

При этом у меня есть все очевидные необходимые вещи, такие как DbContext и сопоставления.

Теперь позвольте сказать, что я создаю объект Reward и вставляю его вот так:

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

У меня, очевидно, есть кампания с Id 1, поэтому ограничение FK радует. После этой вставки мой объект вознаграждения имеет новый идентификатор удостоверения личности. Теперь проблема в том, что вознаграждение по-прежнему остается только созданной мной наградой. И с этим, свойство reward.Campaign равно null. Кажется, что EF сохраняет вставленные объекты в памяти, и когда я затем выполняю.SingleOrDefault(a => a.Id == reward.Id), он просто возвращает объект в памяти, а не новый прокси. Вероятно, это хорошо.

Таким образом, возникает вопрос: как получить или загрузить свойства навигации после вставки или получить новый прокси-сервер, который обладает свойствами навигации в качестве прокси.

Возможно, я не ошибаюсь?

Ответ 1

Если я правильно вас понимаю, вы пытаетесь с нетерпением загрузить сложное свойство после установления отношений через свойство внешнего ключа.

SaveChanges() ничего не делает для загрузки сложных свойств. В лучшем случае он будет устанавливать свой первичный ключ, если вы добавляете новые объекты.

Ваша линия reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id); также не делает ничего, способ загрузки Campaign потому что ваш объект награды не привязан к контексту. Вам нужно явно указать EF, чтобы загрузить этот сложный объект или прикрепить его, а затем пусть ленивая загрузка будет работать с его магией.

Итак, после context.SaveChanges(); у вас есть три варианта загрузки reward.Campaign:

  1. Attach() вознаграждение к контексту, чтобы Campaign можно было лениво загружать (загружать при доступе)

    context.Rewards.Attach(reward);
    

    Примечание. Вы можете получить только ленивое reward.Campaign за загрузку. reward.Campaign в области контекста, поэтому, если вы не собираетесь получать доступ к каким-либо свойствам в течение срока действия контекста, используйте вариант 2 или 3.

  2. Ручная Load() свойство Campaign

    context.Entry(reward).Reference(c => c.Campaign).Load();
    
  3. Вручную Include() свойство Campaign

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

    Хотя, я бы предложил Load так как у вас уже есть reward в памяти.

Ознакомьтесь с разделом "Загрузка связанных объектов" этого документа msdn для получения дополнительной информации.

Ответ 2

Когда вы создаете свой объект reward как new Reward(), EF не имеет прокси. Вместо этого создайте его с помощью DbSet.Create следующим образом:

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

Затем присоедините его к своему DbSet:

context.Rewards.Attach(reward);

Наконец, теперь вы можете использовать ленивую загрузку для получения связанных объектов:

var campaign = reward.Campaign;

Ответ 3

Вы пытались использовать Include()? Что-то вроде этого:

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

Ответ 4

У меня есть простое решение проблемы.

вместо добавления CampaignID в награду добавьте объект кампании.. так:

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

Структура сущности делает весь тяжелый подъем здесь.

Вероятно, вы думаете, что это пустая трата, чтобы загрузить весь объект Кампании, но если вы собираетесь использовать его (по тому, как он выглядит, кажется, вы есть), тогда я не понимаю, почему нет. Вы даже можете использовать оператор include при извлечении его выше, если вам нужно получить доступ к свойствам навигации из объекта кампании...

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