Почему я НЕ получаю исключение Null Reference? - PullRequest
3 голосов
/ 07 марта 2019

Я использую LINQ to Entities для получения данных из базы данных.Ниже мой запрос.

var location = from l in dbContext.Locations
join e in dbContext.Equipment on l.ID equals e.LocationID into rs1
from e in rs1.DefaultIfEmpty()
where ids.Contains(l.ID)
select new
{
    EquipmentClass = e,
    LocationID = l.ID,
    LocationName = l.Name,
    EquipmentName = e == null ? null : e.Name,
    Description = e == null ? null : e.Description,
    InServiceStatus = e == null ? false : e.InServiceStatus,
    EquipmentType = e.EquipmentType.Name
};

foreach (var item in location)
{
    // some logic
}

В приведенном выше коде ids представляет собой список целых чисел, которые я передаю для фильтрации результатов.Когда я получаю результаты, я вижу, что одна из возвращаемых записей имеет значение EquipmentClass.Я выполнил некоторую нулевую проверку, но понял, что забыл выполнить нулевую проверку одного из свойств.Теперь я ожидаю получить исключение нулевой ссылки на EquipmentType = e.EquipmentType.Name, но не получаю.К моему удивлению, он работает просто отлично и имеет значение null.Мой EquipmentClass имеет тип свойства EquipmentType, который является другим классом.EquipmentType имеет свойство Name, которое является строкой.

  1. Почему это не вызывает исключение нулевой ссылки?

Так же, как тест, я удалил проверку нуля из InServiceStatus = e == null ? false : e.InServiceStatus, и она завершается неудачно с недопустимой операциейисключение при запуске цикла foreach с использованием запроса.

Значит ли это, что мне нужно только проверять нулевые значения для ненулевых значений?

Обновление :

foreach (var item in location)
{
    var p = item.EquipmentClass.EquipmentType.Name;
}

Добавлено это сразу послезапрос.При назначении p я получаю исключение нулевой ссылки.Я не уверен, как это вообще заходит так далеко, так как он должен потерпеть неудачу в первой строке цикла foreach.Без строки, объявляющей переменную p, я не получаю исключение нулевой ссылки .Если кто-нибудь может объяснить, что происходит, я был бы благодарен.Просто для справки, значения item.EquipmentClass и item.EquipmentType равны нулю к моменту запуска цикла foreach.

Update2: Я нашел эту ссылку , гдепохоже, что у кого-то есть почти идентичная проблема с использованием LINQ to SQL.Я понимаю суть ответа, но не до конца понимаю его потенциальное влияние на два моих вопроса выше.

Ответы [ 3 ]

5 голосов
/ 12 марта 2019

Если вы пишете запрос LINQ для IQueryable, то происходит следующее: методы, которые вы вызываете за кулисами (например, Select, Where и т. Д.), Не делают ничего, кроме записи как вы их назвали, то есть они записывают выражения предикатов и переносят поставщика LINQ. Как только вы начнете повторять запрос, провайдеру будет предложено выполнить модель запроса . Таким образом, в основном, поставщик использует модель выражений, чтобы дать вам результат ожидаемого типа.

Поставщик ни в коем случае не обязан фактически компилировать или даже выполнять код (модель), который вы предоставили как выражение. Фактически, весь смысл LINQ to SQL или LINQ to Entities заключается в том, что поставщик не делает этого и вместо этого переводит кодовое выражение в SQL.

Следовательно, ваш запрос фактически отображается как запрос SQL, а результат этого запроса переводится обратно. Следовательно, переменная e, которую вы видите в запросе, не обязательно действительно создается, а используется только поставщиком LINQ для компиляции запроса SQL. Однако большинство серверов баз данных имеют нулевое распространение.

Выполните тот же запрос для LINQ to Objects, и вы получите отсутствующее исключение NullReferenceException .

1 голос
/ 09 марта 2019

Ваше обновление помогло мне понять вашу настоящую озабоченность.В отношении запросов LINQ вам необходимо знать концепцию отложенного выполнения в LINQ .

Для получения более подробной информации перейдите по ссылкам ниже:

Что такоеПреимущества отложенного выполнения в LINQ?

Linq - Какой самый быстрый способ узнать отложенное выполнение или нет?

Что теперь происходит в вашем случае?Вы сохранили свой запрос в переменной location.Этот конкретный шаг - только часть инициализации.На самом деле он не выполняет запрос к вашей базе данных через слой ORM.Вот как вы можете это проверить.

Поставьте точку останова на строке кода, где вы инициализируете переменную location запросом LINQ.Когда отладчик остановится в Visual Studio, перейдите в SQL Server Management Studio (SSMS) и запустите сеанс SQL Server Profiler.

Теперь нажмите F10 в Visual Studio, чтобы перейти к инструкции кода.На данный момент вы не увидите абсолютно никакого выполнения запроса в сеансе профилировщика, как показано ниже:

enter image description here

Это все потому, что запрос LINQ этого не сделалвыполняются вообще до этого момента времени.

Теперь вы достигаете строки кода ниже:

foreach (var item in location)
{
    var p = item.EquipmentClass.EquipmentType.Name;
}

В тот момент, когда вы входите в цикл foreach, запрос LINQ запускается, и выВы увидите соответствующий журнал в сеансе трассировки в SQL Server Profiler.Таким образом, запрос LINQ не запускается, если он не перечисляется.Это называется отложенным выполнением, т.е. среда выполнения откладывает выполнение до перечисления.Если вы никогда не перечислите переменную location в своем коде, тогда выполнение запроса никогда не произойдет вообще.

Поэтому, чтобы ответить на ваш запрос, исключение наступит только тогда, когда запрос будет запущен.Не раньше!

Обновление 1 : Вы говорите, что - Я не получаю исключение нулевой ссылки .Да!вы будете не получать нулевое ссылочное исключение, пока не достигнете записи, соответствующая присоединенная запись RHS отсутствует.Посмотрите на приведенный ниже код для лучшего понимания:

class Program
{
    static void Main(string[] args)
    {
        var mylist1 = new List<MyClass1>();
        mylist1.Add(new MyClass1 { id = 1, Name1 = "1" });
        mylist1.Add(new MyClass1 { id = 2, Name1 = "2" });

        var mylist2 = new List<MyClass2>();
        mylist2.Add(new MyClass2 { id = 1, Name2 = "1" });

        var location = from l in mylist1
                       join e in mylist2 on l.id equals e.id into rs1
                       from e in rs1.DefaultIfEmpty()
                       //where ids.Contains(l.ID)
                       select new
                       {
                           EquipmentClass = e,
                           InServiceStatus = e == null ? 1 : e.id,
                           EquipmentType = e.id
                       };

        foreach (var item in location)
        {

        }
    }
}

class MyClass1
{
    public int id { get; set; }
    public string Name1 { get; set; }
}

class MyClass2
{
    public int id { get; set; }

    public string Name2 { get; set; }
}

Итак, теперь, когда я начинаю итерацию переменной location, она не прерывается на первой итерации.Это ломается во второй итерации.Он прерывается, когда не удается получить запись / объект, соответствующий MyClass1 объекту, имеющему id 2 в mylist1.mylist2 не имеет объектов с id 2. Надеюсь, это поможет!

0 голосов
/ 13 марта 2019

ваш e.EquipmentType.Name равен null, и ему присваивается EquipmentType, и вполне нормально назначить null типу nullable, и вы используете DefaultIfEmpty(), который инициализирует элементы с их значение по умолчанию, если оно не соответствует ни одному из условий, и в этом случае ваш e.ElementType.Name устанавливается на null, что, я думаю, вполне нормально используйте ToList(), который выдаст исключение.

Надеюсь, у меня есть смысл, и вы, люди, могли бы обсудить это.

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