LINQ to Entities и нулевые строки
У меня довольно странная вещь, происходящая в веб-приложении ASP.NET 4.0 с использованием EF 4.0 в качестве базы данных. По существу, у меня есть таблица, в которой хранятся пароли пользователей reset (содержащий reset ключ типа byte[]
, истечение срока действия DateTime
и внешний ключ , содержащий a string Email
и string Name
). У некоторых пользователей нет набора адресов электронной почты, поэтому для PasswordRequest request
, request.Email
есть null
.
Вот проблема. Это прекрасно работает:
string u = Request["u"];
string e = Request["e"];
var requests = from r in context.PasswordRequests
where r.User.Name == u && r.User.Email == null && r.Expiry >= DateTime.Now
select r;
Я получаю ожидаемое количество результатов (отличное от нуля, поскольку есть записи с null
сообщениями электронной почты).
Но это всегда возвращает пустую коллекцию, когда e
есть null
:
string u = Request["u"];
string e = Request["e"];
var requests = from r in context.PasswordRequests
where r.User.Name == u && r.User.Email == e && r.Expiry >= DateTime.Now
select r;
Единственное, что мне нужно для правильной работы (что логически не имеет смысла), следующее:
string u = Request["u"];
string e = Request["e"];
IQueryable<PasswordRequest> requests;
if (e == null)
requests = from r in context.PasswordRequests
where r.User.Name == u && r.User.Email == null && r.Expiry >= DateTime.Now
select r;
else
requests = from r in context.PasswordRequests
where r.User.Name == u && r.User.Email == e && r.Expiry >= DateTime.Now
select r;
Я абсолютно в тупике. Любые идеи?
Ответы
Ответ 1
В основном это несоответствие между SQL и С#, когда дело касается обработки нулей. Вам не нужно использовать два запроса, но вам нужно:
where r.User.Name == u && (r.User.Email == e ||
(e == null && r.User.Email == null))
Это раздражает, и может быть вспомогательная функция, облегчающая жизнь, но она в основном исходит от обработки SQL-кода, где
where X = Y
не будет совпадать, если оба X и Y равны нулю. (В то время как в С# эквивалентное выражение будет истинным.)
Возможно, вам понадобится сделать то же самое для u
, если только это не является нулевым в базе данных.
Один небольшой трюк, который вы могли бы хотя бы попробовать, если вы довольны пустым и пустым строками, обрабатываемым таким же образом:
// Before the query
e = e ?? "";
// In the query
where r.User.Name == u && (r.User.Email ?? "") == e
Я считаю, что будет выполнять нулевое коалесценцию как по столбцу электронной почты, так и по e
, поэтому вы никогда не сравниваете нуль ни с чем.
Ответ 2
Я нашел пару статей, в которых описывается одна и та же проблема. К сожалению, до сих пор я не сталкивался с этой проблемой. Это очень интересно.
Здесь:
Синтаксис LINQ, где строковое значение не является нулевым или пустым
LINQ to SQL и Null, как использовать Contains?
И из MSDN: http://msdn.microsoft.com/en-us/library/bb882535.aspx
Ответ 3
Если вы предпочитаете использовать синтаксис метода (лямбда), как я, вы можете сделать это следующим образом:
var result = new TableName();
using(var db = new EFObjectContext)
{
var query = db.TableName;
query = value1 == null
? query.Where(tbl => tbl.entry1 == null)
: query.Where(tbl => tbl.entry1 == value1);
query = value2 == null
? query.Where(tbl => tbl.entry2 == null)
: query.Where(tbl => tbl.entry2 == value2);
result = query
.Select(tbl => tbl)
.FirstOrDefault();
// Inspect the value of the trace variable below to see the sql generated by EF
var trace = ((ObjectQuery<REF_EQUIPMENT>) query).ToTraceString();
}
return result;
Ответ 4
Если вы хотите получить элементы из БД при запросе ['e'] == null
он должен был
var requests = from r in context.PasswordRequests
where r.User.Name == u && r.User.Email is null && r.Expiry >= DateTime.Now
select r;
Обратите внимание, что == null и имеет значение null. см. → Информация о MSDN
Поэтому ваш последний пример является своего рода действительным, так как вам нужно два способа получить данные из БД. то есть, если адрес электронной почты является нулевым, а один, если адрес электронной почты == Запрос ['e']