EF4 linq NullReferenceException для ненулевого объекта - PullRequest
3 голосов
/ 12 ноября 2010

Сначала я использую код ef4 с общим репозиторием.В моем репозитории есть метод select, который выглядит следующим образом:

public IEnumerable<T> Select(Func<T, bool> predicate)
{
    return objectSet.Where(predicate);
}

Я вызываю его со следующим кодом

pushQueueRepository.Select(x => x.User.ID == user.ID && x.PageID == pageID);

* note - pushQueueRepository был правильно создан.

Когда я запускаю это, я получаю исключение NullReferenceException.Когда я смотрю на него в отладке после того, как генерируется исключение, это показывает, что ошибка x.User.ID == user.ID.Когда я наведите курсор мыши на x.User, он будет нулевым.Однако, когда я раскрываю x, мы получаем объект User в x.User (не нуль), который имеет идентификатор.

FYI x - это объект PushQueue, который определен так:

public class PushQueue : IEntity
{
    ...

    [Required]
    public User User { get; set; }

    ... 
}

Это кажется неправильным, я что-то упустил?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 12 ноября 2010

Причина получения исключения заключается в том, что вы загружаете все PushQueues в память, а затем пытаетесь применить свой предикат: x => x.User.ID == user.ID, и потому что отложенная загрузка НЕ включенапо вашему коду x.User загружаться не будет, поэтому создается исключение.Вы не пометили свойство навигации пользователя как virtual , поэтому оно не включило EF Lazy Loading.Когда вы развертываете его в режиме отладки в VS, вы явно загружаете его, но во время выполнения он загружается не лениво, и поэтому вы видите, что он заполняется при расширении.

Чтобы исправить это, вам нужно изменить сигнатуру вашего метода Select, так как это основная проблема: вы передаете Func<T, bool>, тогда как вместо этого необходимо указать Expression<Func<T, bool>>.По сути, вы хотите, чтобы ваш предикат выполнялся в хранилище данных, а не в памяти, поэтому вам нужно изменить код так:

public IEnumerable<T> Select(Expression<Func<T, bool>> predicate)
{
    return objectSet.Where(predicate);
}

Конечно, в качестве альтернативы вы можете оставить метод выбора таким, какой он есть сейчас, ивключите отложенную загрузку, таким образом, исключение NullReferenceException исчезнет, ​​но это приведет к ужасной производительности во время выполнения, так как EF будет пытаться загружать User на каждый отдельный объект PushQueue, а затем применять свой предикат:

0 голосов
/ 12 ноября 2010

Я думаю, вам нужно использовать .Include:

pushQueueRepository.Include("User").Select(x => x.User.ID == user.ID && x.PageID == pageID)

.Include заставляет EF сразу загружать объект User, который в противном случае был бы загружен лениво и, следовательно, недоступен для сравнения по User.ID.

Наличие строки в .Include не очень элегантно и, к сожалению, не проверено во время компиляции. Вы можете сделать что-то вроде этого:

pushQueueRepository.Include(typeof(User).Name)....

Не очень элегантно, но, по крайней мере, это проверяется компилятором; -)

0 голосов
/ 12 ноября 2010

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

Конечно, если это EF-репозиторий, я ожидаю фактический запросв любом случае будет выполняться в базе данных, так что то, что вы видите в случае сбоя запроса, все равно несколько необычно.Вы пытались посмотреть, какой запрос выполняется в SQL?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...