2010-05-16 3 views
3

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

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.

ответ

10

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

The conditional-AND operator (&&) performs a logical-AND of its bool operands, but only evaluates its second operand if necessary.

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


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

  • С помощью DataContext.Log можно записывать операторы SQL.
  • Ваш запрос не должен генерировать ошибку, независимо от того, в каком направлении вы ее пишете.
  • Существуют различия в поведении между LINQ и объектами и LINQ to 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 имеет значение null, 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 к объектам с NullReferenceException, но он работает с LINQ к SQL, не бросать исключение. Поэтому я подозреваю, что вы загрузили все элементы из базы данных в память и фильтруетесь локально.Другими словами, может быть, у вас есть что-то вроде этого:

var userList = dc.Users.ToList(); 

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

var userList = dc.Users; 

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

+0

Хороший ответ - сладкие и простые –

+0

спасибо ... – userb00

+0

@Preet Сангха : Не так уж и коротко. Глядя на это более близко, я не уверен, что простой ответ объясняет поведение полностью. –

0

Что касается вопроса о том, как это переводит 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) 
+0

Я не думаю, что SQL имеет ту же короткозамкнутую семантику, что и C#. См. Этот вопрос http://stackoverflow.com/questions/2842334/linq-query-checks-for-null/2842340#2842340 и это сообщение: http://weblogs.sqlteam.com/mladenp/archive/2008/02/ 25/How-SQL-Server-short-circuit-WHERE-condition-evaluation.aspx –

+0

@Mark: Интересно - возможно, в этом случае это не имеет значения, потому что SQL-запрос не сбой при работе с 'null' .. , –

Смежные вопросы