LINQ запрос проверяет наличие нуля - PullRequest
4 голосов
/ 16 мая 2010

У меня есть userList, у некоторых пользователей нет имени (null). Если я запускаю первый запрос LINQ, я получаю сообщение об ошибке «Ссылка на объект не установлена ​​на экземпляр объекта».

var temp = (from a in userList
            where ((a.name == "john") && (a.name != null))
            select a).ToList();

Однако, если я переключаю ордер, помещая проверку на ноль впереди, тогда он работает без каких-либо ошибок:

var temp = (from a in userList
            where ((a.name != null) && (a.name == "john"))
            select a).ToList();

Почему это? Если это чистый код C # (не LINQ), я думаю, что оба будут одинаковыми. У меня нет профилировщика SQL, мне просто интересно, какая разница, когда они переводятся на уровне SQL.

Ответы [ 2 ]

11 голосов
/ 16 мая 2010

В C # оператор && имеет короткое замыкание, поэтому, если первое условие возвращает false, второе условие вообще не выполняется. Из MSDN:

Оператор условного «И» (&&) выполняет логическое «И» своих булевых операндов, но только при необходимости оценивает свой второй операнд.

Оператор || ведет себя аналогичным образом, за исключением того, что он не оценивает свой второй аргумент, если первый возвращает true.


Я не думаю, что это полная история. Остальная часть моего поста охватывает следующие моменты:

  • Вы можете регистрировать операторы SQL, используя DataContext.Log.
  • Ваш запрос не должен генерировать ошибку независимо от того, в какую сторону вы ее пишете.
  • Существуют различия в поведении между LINQ для объектов и LINQ для SQL.
  • Ваша фильтрация может выполняться локально, а не в базе данных.

Вы можете легко просмотреть сгенерированный SQL в Visual Studio без использования профилировщика SQL. Вы можете навести указатель мыши на объект запроса LINQ to SQL, и он отобразит SQL. Или вы можете использовать DataContext.Log для записи операторов SQL, например, так:

TextWriter textWriter = new StringWriter();
using (var dc = new UserDataContext())
{
    dc.Log = textWriter;
    var userList = dc.Users;
    var temp = (from a in userList
                where (a.Name.ToString() == "john") && (a.Name != null)
                select a).ToList();
}
string log = textWriter.ToString();

Вы также можете войти в файл или даже Console.Out:

dc.Log = Console.Out;

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

SELECT [t0].[Name]
FROM [dbo].[User] AS [t0]
WHERE ([t0].[Name] = @p0) AND ([t0].[Name] IS NOT NULL)

Другое дело, что ваш запрос не должен генерировать ошибку. Даже если a.name равен нулю, a == "john" все равно должен работать - он просто вернет false.

Наконец, есть разница между тем, как обычно работает C #, и тем, как работает LINQ to SQL. Вы не должны получать нулевое исключение из базы данных. Чтобы продемонстрировать это, я сделаю небольшую модификацию вашего запроса - добавив ToString после a.Name:

var temp = (from a in userList
            where (a.Name.ToString() == "john") && (a.Name != null)
            select a).ToList();

Теперь это невозможно для Linq to Objects с NullReferenceException, но он работает с LINQ to SQL без исключения. Поэтому я подозреваю, что вы загрузили все элементы из базы данных в память и выполняете локальную фильтрацию. Другими словами, может быть, у вас есть что-то вроде этого:

var userList = dc.Users.ToList();

вместо следующего, который позволил бы базе данных выполнять фильтрацию:

var userList = dc.Users;

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

0 голосов
/ 16 мая 2010

Что касается вопроса, как это переводит SQL - я думаю, что SQL имеет ту же семантику короткого замыкания, поэтому переводчик SQL просто сохраняет порядок условий в сгенерированном запросе.

Например, следующие два предложения LINQ:

where p.CategoryID != null && p.CategoryID.Value > 1
where p.CategoryID.Value > 1 && p.CategoryID != null

Перевести на следующие два предложения SQL:

WHERE ([t0].[CategoryID] IS NOT NULL) AND (([t0].[CategoryID]) > @p0)
WHERE (([t0].[CategoryID]) > @p0) AND ([t0].[CategoryID] IS NOT NULL)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...