Reputation: 474
I've got quite a strange thing happening on an ASP.NET 4.0 web application using EF 4.0 as its database backend. Essentially, I've got a table that stores users' password reset requests (containing a reset key of type byte[]
, an expiry of type DateTime
, and a foreign key to a User
containing a string Email
and string Name
). Some users do not have an email address set, so for a PasswordRequest request
, request.Email
is null
.
Here's the problem. This works perfectly fine:
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;
I get the expected number of results (nonzero, since there are entries with null
emails).
But this always returns an empty collection when e
is 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;
The only thing that I got to work properly (which doesn't logically make any sense) is this:
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;
I'm absolutely stumped. Any ideas?
Upvotes: 15
Views: 14031
Reputation: 2356
If you prefer using method (lambda) syntax as I do, you could do it like this:
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;
Upvotes: 1
Reputation: 9
If you want to retrieve items from the DB when request['e'] == null
it should have been
var requests = from r in context.PasswordRequests
where r.User.Name == u && r.User.Email is null && r.Expiry >= DateTime.Now
select r;
note that == null and is null is different . see --> MSDN Info
Therefore, your last example is sort of valid since you need 2 ways to get data from the DB. i.e. one if email is null and one if email == Request['e']
Upvotes: -1
Reputation: 5042
I have found a couple of articles detailing the same issue. Unfortunately, I haven't faced this issue so far. It is very interesting though.
Here:
LINQ syntax where string value is not null or empty
LINQ to SQL and Null strings, how do I use Contains?
And from MSDN: http://msdn.microsoft.com/en-us/library/bb882535.aspx
Upvotes: 3
Reputation: 1500505
Basically this is a mismatch between SQL and C# when it comes to the handling of nulls. You don't need to use two queries, but you need:
where r.User.Name == u && (r.User.Email == e ||
(e == null && r.User.Email == null))
It's annoying, and there may be a helper function to make life easier, but it fundamentally comes from SQL's null handling where
where X = Y
will not match if both X and Y are null. (Whereas in C# the equivalent expression would be true.)
You may need to do the same for u
as well, unless that is non-nullable in the database.
One small trick you could at least try if you're happy with null and empty strings being handled the same way is:
// Before the query
e = e ?? "";
// In the query
where r.User.Name == u && (r.User.Email ?? "") == e
I believe that will perform null coalescing on both the email column and e
, so you never end up comparing null with anything.
Upvotes: 37