Linq-to-SQL генерирует исключение NullReferenceException, несмотря на проверку на нулевые аргументы - PullRequest
0 голосов
/ 20 декабря 2009

Я пытаюсь написать запросы Linq-To-SQL в LinqPad, чтобы помочь перенести пользователей из старых таблиц в новые. Часть этой миграции хранит все адреса в отдельной таблице. Я использую следующий запрос, чтобы определить, существует ли адрес пользователя в новой таблице (поэтому у меня нет повторяющихся записей):

var addresses = from a in Addresses where ((u.Street_address == null && a.Street1 == null) || (u.Street_address != null && a.Street1 != null && a.Street1.ToLower() == u.Street_address.ToLower())) 
                                && ((a.City == null && u.City == null) || (a.City != null && u.City != null && a.City.ToLower() == u.City.ToLower())) 
                                && ((a.State == null && u.State == null) || (a.State != null && u.State != null && a.State.ToLower() == u.State.ToLower())) 
                                && ((a.Zip == null && u.Zipcode == null) || (a.Zip != null && u.Zipcode != null && a.Zip.ToLower() == u.Zipcode.ToLower()))
                                select a;

Здесь 'u' представляет старого пользователя. Некоторые адреса в старой таблице содержат пустые записи для Street_address, City, State и / или Zipcode. Кроме того, некоторые адреса являются дубликатами, кроме кожуха (отсюда и ToLower ()).

Несмотря на проверку нулевых параметров в запросе, я все равно получаю исключение NullReferenceException, если какой-либо из параметров адреса пользователя имеет значение null.

Я что-то не так делаю? Или есть лучший способ выполнить то, что мне нужно?

1 Ответ

2 голосов
/ 20 декабря 2009

UPDATE:

Хммм, похоже, это сложнее, чем я думал. Оказывается, String.Equals с перегрузкой StringComparison не поддерживается Linq-to-SQL . Но тот факт, что вы получаете ошибку, означает, что Linq-to-SQL пытается взять все выражение и превратить его в SQL. Это, в свою очередь, означает, что все сравнения будут выполняться в соответствии с исходным сопоставлением базы данных, которое по умолчанию не учитывает регистр. Поэтому, хотя Linq-to-SQL не будет поддерживать сравнения без учета регистра, вам, вероятно, не нужны сравнения без учета регистра, поскольку вы можете полагаться на то, что SQL Server делает их по умолчанию.

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

var addresses = from a in Addresses 
                where String.Equals (u.Street_address, a.Street1)
                      && String.Equals (u.City, a.City)
                      && String.Equals (u.State, a.State)
                      && String.Equals (u.ZipCode, a.Zip)
                select a;

Возможно, это тоже может сработать:

var addresses = from a in Addresses 
                where u.Street_address == a.Street1
                      && u.City == a.City
                      && u.State == a.State
                      && u.ZipCode == a.Zip
                select a;

Но, исходя из моего прочтения этой статьи MSDN (выдержка ниже), я подозреваю, что только использование == (вместо String.Equals () может не работать):

Нулевая семантика

LINQ to SQL не наложить нулевую семантику сравнения на SQL. Операторы сравнения синтаксически переводится на их SQL эквиваленты. По этой причине семантика отражает семантику SQL, которая определяются сервером или соединением Настройки. Например, два нулевых значения считаются неравными по умолчанию Настройки SQL Server, но вы можете изменить настройки, чтобы изменить семантика. LINQ to SQL не имеет учитывать настройки сервера, когда это переводит запросы.

Сравнение с буквальным нулем переведено на соответствующий SQL версия (нулевая или не нулевая).

Другими словами, если я правильно читаю этот текст MSDN, звучит так, будто Linq-to-SQL переводит == в = в T-SQL, в то время как (как показал ваш эксперимент) String.Equals переводится правильно как проверка для IS NULL с последующей проверкой с использованием =. Если у вас есть возможность протестировать только ==, мне было бы интересно узнать, отправляет ли Linq-to-SQL проверки IS NULL или нет.

Учитывая сложность здесь (Linq-to-SQL переводит C # в SQL и приводит к C #), ваш лучший выбор в подобных случаях - попробовать несколько вариантов (например, == против Equals ()) и выбрать один это работает, так как есть достаточно движущихся частей, которые трудно предвидеть заранее, какой вариант будет работать лучше всего.

СТАРЫЙ ОТВЕТ (игнорируйте это):

Попробуйте использовать статический метод String.Equals вместо == и ToLower(). Вы избежите проблем с нулевыми ссылками (и упростите свой код), потому что пустые значения допустимы для передачи в этот метод, и он поддерживает проверку без учета регистра.

var addresses = from a in Addresses 
                where String.Equals (u.Street_address, a.Street1, StringComparison.OrdinalIgnoreCase)
                      && String.Equals (u.City, a.City, StringComparison.OrdinalIgnoreCase)
                      && String.Equals (u.State, a.State, StringComparison.OrdinalIgnoreCase)
                      && String.Equals (u.ZipCode, a.Zip, StringComparison.OrdinalIgnoreCase)
                select a;

Хотя, если ваша база данных уже нечувствительна к регистру, в зависимости от того, как Linq-to-SQL разделяет работу между кодом SQL и C #, вам может вообще не понадобиться проверка нечувствительности - хотя, если бы это был я, я бы скорее будьте в безопасности и убедитесь, что вы всегда проводите проверку по делу.

...