Подтвердить что ты не робот

Entity Framework: я устанавливаю внешний ключ, SaveChanges затем получает доступ к свойству навигации, но он не загружает связанный объект. Почему нет?

Я использую этот класс Entity с Entity Framework 5 Code First:

public class Survey
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public string SurveyName { get; set; }

    [Required]
    public int ClientID { get; set; }

    [ForeignKey("ClientID")]
    public virtual Client Client { get; set; }
}

И в моем методе Create Control я делаю это:

    Survey entity = new Survey()
    {
        SurveyName = "Test Name",
        ClientID = 4
    };
    db.Surveys.Add(entity);
    db.SaveChanges();
    Client c1 = entity.Client;                    //Why is this null?
    Client c2 = db.Clients.Find(entity.ClientID); //But this isn't?

    string s2 = c2.ClientName;
    string s1 = c1.ClientName;   //null reference thrown here

Свойство навигации клиента остается null после SaveChanges. Я ожидал вызова для загрузки Клиента из базы данных, поскольку существует внешний ключ. Почему он этого не сделал?

ИЗМЕНИТЬ Код здесь возникает, когда мои контроллеры зависят от DbContext. Вскоре после того, как я получил эту работу, я повторно использовал код для использования репозиториев и единицы работы. Часть этого движения была обусловлена ​​тем фактом, что было просто неправильно использовать Create, когда я хотел использовать new. Тогда произошло то, что я столкнулся с проблемой как обеспечить, чтобы прокси создавались при использовании шаблона репозитория.

4b9b3361

Ответ 1

Чтобы гарантировать, что ленивая загрузка свойства навигации будет работать после того, как вы создали родителя, вы не должны создавать Survey с помощью оператора new, а создавать его с помощью экземпляра контекста, поскольку он будет создавать экземпляр динамического прокси, который способен лениво загрузить связанный Client. Это метод DbSet<T>.Create() для:

Survey entity = db.Surveys.Create();

entity.SurveyName = "Test Name";
entity.ClientID = 4;

db.Surveys.Add(entity);
db.SaveChanges();

Client c1 = entity.Client;
string s1 = c1.ClientName;
// will work now if a Client with ID 4 exists in the DB

Просто подчеркнуть: это не строка entity.ClientID = 4; или db.Surveys.Add(entity); или db.SaveChanges, которая загружает клиента из БД, а строку Client c1 = entity.Client; (ленивая загрузка).

Ответ 2

Как сказал @NicholasButler, вызов SaveChanges делает то, что он говорит на жесте - вы можете увидеть это, если вы отлаживаете свой код: вывод Intellitrace покажет SQL, который он сгенерировал для вставки/обновления, который вы сохраняете, но последующего выбора не будет.

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

Структура Entity Framework (начиная с версии 4.1 и выше) поддерживает ленивую загрузку. Это означает, что если он включен, код типа Client c1 = entity.Client; должен загрузить этот объект Client. Чтобы быть ясным, эта операция напрямую не связана с вызовом SaveChanges.

Платит, чтобы проверить, установлено ли db.Configuration.LazyLoadingEnabled на true. Если нет, попробуйте установить его как true и посмотрите, остается ли Client c1 = entity.Client; null.

Короче говоря, вызов SaveChanges не вызывает загрузку, но если включена ленивая загрузка, доступ к entity.Client должен инициировать загрузку объекта, если он еще не был загружен.

Edit:

Я должен был хотя бы об этом раньше, но вы не будете получать ленивую нагрузку на свой объект Survey entity. Причина в том, что EF работает с ленивой магией загрузки, создавая класс, полученный из вашего, но переопределяя свойства, помеченные как virtual, для поддержки ленивой загрузки. Он делает это, когда вы выполняете поиск, поэтому ваш объект entity не будет ленить загружать все, что есть.

Попробуйте это сразу после вашего вызова SaveChanges:

Survey entity2 = db.Surveys.Find(entity.ID);
Client c1 = entity2.Client;

Это должно проявлять поведение, которое вы после.

Ответ 4

Я ожидал вызова для загрузки Клиента из базы данных, поскольку существует внешний ключ. Почему он этого не сделал?

Это не сделало этого, потому что вы этого не спрашивали. После вызова SaveChanges() EF не имеет данных в ссылочной строке, и он не сделает потенциально избыточный вызов базы данных, чтобы получить его.

Вызов db.Clients.Find(... говорит EF, чтобы он выходил и извлекал строку из базы данных, поэтому он возвращает объект.