111

I want to execute a query like this

   var result = from entry in table
                     where entry.something == null
                     select entry;

and get an IS NULL generated.

Edited: After the first two answers i feel the need to clarify that I'm using Entity Framework and not Linq to SQL. The object.Equals() method does not seem to work in EF.

Edit no.2: The above query works as intended. It correctly generates IS NULL. My production code however was

value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;

and the generated SQL was something = @p; @p = NULL. It seems that EF correctly translates the constant expression but if a variable is involved it treats it just like a normal comparison. Makes sense actually. I will close this question.

peterh
  • 11,875
  • 18
  • 85
  • 108
Adrian Zanescu
  • 7,907
  • 6
  • 35
  • 53
  • 18
    I think it doesn't really makes sense... The connector should be a little smart and not asking us to do its job : perform a correct translation in SQL of correct C# query. This generates an unexpected behaviour. – Julien N May 17 '10 at 09:06
  • 6
    I am with Julien, this is a failure on the part of EF – Mr Bell Jul 28 '11 at 15:55
  • 1
    This is a failure of the standards, and it's only getting worse now that comparison against null is permanently resulting in undefined as of SQL Server 2016 with ANSI NULLs permanently set to on. Null may *represent* an unknown value, but "null" itself is not an unknown value. Comparison of a null value with a null value should absolutely yield true, but unfortunately the standard departs from common sense as well as Boolean logic. – Triynko Oct 05 '15 at 19:19

14 Answers14

125

Workaround for Linq-to-SQL:

var result = from entry in table
             where entry.something.Equals(value)
             select entry;

Workaround for Linq-to-Entities (ouch!):

var result = from entry in table
             where (value == null ? entry.something == null : entry.something == value)
             select entry;

This is a nasty bug which has bitten me several times. If this bug has affected you too, please visit the bug report on UserVoice and let Microsoft know that this bug has affected you as well.


Edit: This bug is being fixed in EF 4.5! Thanks everyone for upvoting this bug!

For backwards compatibility, it will be opt-in - you need manually enable a setting to make entry == value work. No word yet on what this setting is. Stay tuned!


Edit 2: According to this post by the EF team, this issue has been fixed in EF6! Woohoo!

We changed the default behavior of EF6 to compensate for three-valued logic.

This means that existing code that relies on the old behavior (null != null, but only when comparing to a variable) will either need to be changed to not rely on that behavior, or set UseCSharpNullComparisonBehavior to false to use the old broken behavior.

Taco
  • 181
  • 1
  • 12
BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • 6
    I've voted up the bug report. Hopefully they fix this. I can't say that I really remember this bug being present in vs2010 beta... – noobish Oct 06 '10 at 19:36
  • 2
    oh come on microsoft... really?!?!? In version 4.1?!?! +1 – David Aug 01 '11 at 18:00
  • 1
    That Linq-To-SQL workaround doesn't seem to work (trying with a Guid?). Using the Entities-Workaround works in L2S, but generates horrendous SQL. I had to do an if-statement in code `(var result = from ...; if(value.HasValue) result = result.Where(e => e.something == value) else result = result.Where(e => e.something == null);` – Michael Stum Oct 31 '11 at 01:37
  • 5
    Object.Equals works actually `(where Object.Equals(entry.something,value))` – Michael Stum Oct 31 '11 at 01:38
  • 1
    I'm using EF v5.0RC and its not been fixed!! http://stackoverflow.com/questions/11458514/where-clause-with-null-object-using-entity-framework-v5-0rc – YodasMyDad Jul 12 '12 at 18:47
  • @leen3o: I haven't tried it yet, but there is supposedly supposed to be an option to enable C#-like null comparisons. Search around, and if you find the option *(in the entity-context maybe?)*, please post back here and I will update my answer :) If not, I will update it when I get time to look into it. – BlueRaja - Danny Pflughoeft Jul 12 '12 at 19:28
  • This doesn't always work in EF. You will get an Unable to cast non-edm primitive types error. It will work if the constant value you are comparing against is null. But if that constant has a value, it will error on the attempt to cast the data to an Object. – Steven Pena Sep 12 '12 at 08:38
  • @Steven: Do you mean the *"Workaround for Linq-to-Entities,"* above, or the `Object.Equals` method? I wasn't even aware that had been edited into the answer... – BlueRaja - Danny Pflughoeft Sep 12 '12 at 08:49
  • Yes, the workaround described in the edited answer for Linq-to-Entities doesn't always work. – Steven Pena Sep 21 '12 at 21:38
  • @StevenPena: Thank you, I've removed it. – BlueRaja - Danny Pflughoeft Sep 21 '12 at 22:39
  • I don't see this fixed in EF5, also I cannot find any sign of any option to enable this 'feature'. – Oskar Sjöberg Nov 14 '12 at 21:31
  • @OskarSjöberg: I haven't used EF 4.5 yet, but is [this](http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontextoptions.usecsharpnullcomparisonbehavior.aspx) it? – BlueRaja - Danny Pflughoeft Nov 14 '12 at 21:50
  • @BlueRaja-DannyPflughoeft: I have downloaded EF5 with nuget in VS2010, but I cannot access that property from my DbContext. It is simply not there. Any clue? – Oskar Sjöberg Nov 15 '12 at 22:51
  • 5
    @leen3o (or anyone else) - Has anyone yet found where this alleged fix is in EF 4.5/5.0? I'm using 5.0 and it's still misbehaving. – Shaul Behr Jan 27 '13 at 14:53
  • I'm not sure when this was fixed, but it happened for me in EntityFramework 6.0.2. I just updated to 6.1.3 and it is fixed. – David Hammond May 01 '15 at 18:13
17

Since Entity Framework 5.0 you can use following code in order to solve your issue:

public abstract class YourContext : DbContext
{
  public YourContext()
  {
    (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
  }
}

This should solve your problems as Entity Framerwork will use 'C# like' null comparison.

ITmeze
  • 1,902
  • 2
  • 21
  • 32
16

There is a slightly simpler workaround that works with LINQ to Entities:

var result = from entry in table
         where entry.something == value || (value == null && entry.something == null)
         select entry;

This works becasuse, as AZ noticed, LINQ to Entities special cases x == null (i.e. an equality comparison against the null constant) and translates it to x IS NULL.

We are currently considering changing this behavior to introduce the compensating comparisons automatically if both sides of the equality are nullable. There are a couple of challenges though:

  1. This could potentially break code that already depends on the existing behavior.
  2. The new translation could affect the performance of existing queries even when a null parameter is seldom used.

In any case, whether we get to work on this is going to depend greatly on the relative priority our customers assign to it. If you care about the issue, I encourage you to vote for it in our new Feature Suggestion site: https://data.uservoice.com.

divega
  • 6,320
  • 1
  • 31
  • 31
9

If it is a nullable type, maybe try use the HasValue property?

var result = from entry in table
                 where !entry.something.HasValue
                 select entry;

Don't have any EF to test on here though... just a suggestion =)

Svish
  • 152,914
  • 173
  • 462
  • 620
  • 1
    Well... this only works if you're just looking for nulls, but then using `== null` doesn't get hit by the bug anyway. The point is to be filtering by the value of a variable, whose value might be null, and have the null value find the null records. – Dave Cousineau Apr 19 '13 at 22:31
  • 1
    Your answer saved me. I forgot to use a nullable type on my entity model class and couldn't make `(x => x.Column == null)` to work. :) – Reuel Ribeiro Dec 11 '17 at 16:43
  • This gives `System.NullReferenceException `, since the object allready is null! – TiyebM Jun 23 '20 at 11:04
5
var result = from entry in table
             where entry.something.Equals(null)
             select entry;

MSDN Reference: LINQ to SQL: .NET Language-Integrated Query for Relational Data

Konstantin Tarkus
  • 37,618
  • 14
  • 135
  • 121
5

to deal with Null Comparisons use Object.Equals() instead of ==

check this reference

bdukes
  • 152,002
  • 23
  • 148
  • 175
Oscar Cabrero
  • 4,168
  • 8
  • 29
  • 49
  • This works perfectly in Linq-To-Sql and also generates the proper SQL (some other answers here generate horrendous SQL or wrong results). – Michael Stum Oct 31 '11 at 01:54
  • Suppose I want to compaire with `null`, `Object.Equals(null)`, what if the `Object` itself is null? – TiyebM Jun 23 '20 at 11:08
4

Pointing out that all of the Entity Framework < 6.0 suggestions generate some awkward SQL. See second example for "clean" fix.

Ridiculous Workaround

// comparing against this...
Foo item = ...

return DataModel.Foos.FirstOrDefault(o =>
    o.ProductID == item.ProductID
    // ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
    && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null
    && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null
    && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null
    && o.Width == w
    && o.Height == h
    );

results in SQL like:

SELECT TOP (1) [Extent1].[ID]                 AS [ID],
       [Extent1].[Name]               AS [Name],
       [Extent1].[DisplayName]        AS [DisplayName],
       [Extent1].[ProductID]          AS [ProductID],
       [Extent1].[ProductStyleID]     AS [ProductStyleID],
       [Extent1].[MountingID]         AS [MountingID],
       [Extent1].[Width]              AS [Width],
       [Extent1].[Height]             AS [Height],
       [Extent1].[FrameID]            AS [FrameID],
FROM   [dbo].[Foos] AS [Extent1]
WHERE  (CASE
  WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */)
        AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit)
      WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[ProductStyleID] IS NULL)
        AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit)
      WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[MountingID] IS NULL)
        AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit)
      WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[FrameID] IS NULL)
        AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
        AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit)
  WHEN (NOT (([Extent1].[FrameID] IS NULL)
             AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
             AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit)
END) = 1

Outrageous Workaround

If you want to generate cleaner SQL, something like:

// outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
Expression<Func<Foo, bool>> filterProductStyle, filterMounting, filterFrame;
if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID;
else filterProductStyle = o => o.ProductStyleID == null;

if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID;
else filterMounting = o => o.MountingID == null;

if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID;
else filterFrame = o => o.FrameID == null;

return DataModel.Foos.Where(o =>
    o.ProductID == item.ProductID
    && o.Width == w
    && o.Height == h
    )
    // continue the outrageous workaround for proper sql
    .Where(filterProductStyle)
    .Where(filterMounting)
    .Where(filterFrame)
    .FirstOrDefault()
    ;

results in what you wanted in the first place:

SELECT TOP (1) [Extent1].[ID]                 AS [ID],
           [Extent1].[Name]               AS [Name],
           [Extent1].[DisplayName]        AS [DisplayName],
           [Extent1].[ProductID]          AS [ProductID],
           [Extent1].[ProductStyleID]     AS [ProductStyleID],
           [Extent1].[MountingID]         AS [MountingID],
           [Extent1].[Width]              AS [Width],
           [Extent1].[Height]             AS [Height],
           [Extent1].[FrameID]            AS [FrameID],
FROM   [dbo].[Foos] AS [Extent1]
WHERE  ([Extent1].[ProductID] = 1 /* @p__linq__0 */)
   AND ([Extent1].[Width] = 16 /* @p__linq__1 */)
   AND ([Extent1].[Height] = 20 /* @p__linq__2 */)
   AND ([Extent1].[ProductStyleID] IS NULL)
   AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */)
   AND ([Extent1].[FrameID] IS NULL)
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • The code running on the SQL will be cleaner and faster but EF will generate & cache new query plan for every combination before send it to sql server, which makes it slower than other workarounds. – Burak Tamtürk Jun 22 '16 at 03:20
2
var result = from entry in table
                     where entry.something == null
                     select entry;

The above query works as intended. It correctly generates IS NULL. My production code however was

var value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;

and the generated SQL was something = @p; @p = NULL. It seems that EF correctly translates the constant expression but if a variable is involved it treats it just like a normal comparison. Makes sense actually.

Adrian Zanescu
  • 7,907
  • 6
  • 35
  • 53
1

Personnally, I prefer:

var result = from entry in table    
             where (entry.something??0)==(value??0)                    
              select entry;

over

var result = from entry in table
             where (value == null ? entry.something == null : entry.something == value)
             select entry;

because it prevents repetition -- though that's not mathematically exact, but it fits well most cases.

1

It appears that Linq2Sql has this "problem" as well. It appears that there is a valid reason for this behavior due to whether ANSI NULLs are ON or OFF but it boggles the mind why a straight "== null" will in fact work as you'd expect.

JasonCoder
  • 1,126
  • 2
  • 12
  • 24
0

Unfortunately in Entity Framework 5 DbContext the issue is still not fixed.

I used this workaround (works with MSSQL 2012 but ANSI NULLS setting might be deprecated in any future MSSQL version).

public class Context : DbContext
{

    public Context()
        : base("name=Context")
    {
        this.Database.Connection.StateChange += Connection_StateChange;
    }

    void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e)
    {
        // Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework
        // that is not fixed in EF 5 when using DbContext.
        if (e.CurrentState == System.Data.ConnectionState.Open)
        {
            var connection = (System.Data.Common.DbConnection)sender;
            using (var cmd = connection.CreateCommand())
            {
                cmd.CommandText = "SET ANSI_NULLS OFF";
                cmd.ExecuteNonQuery();
            }
        }
    }
}

It should be noted that it is a dirty workaround but it is one that can be implemented very quickly and works for all queries.

Knaģis
  • 20,827
  • 7
  • 66
  • 80
  • This will immediately cease to function once ANSI NULLS is permanently set to ON in a future version of SQL Server, in case the warning wasn't clear. – Triynko Oct 05 '15 at 19:30
0

If you prefer using method (lambda) syntax as I do, you could do the same thing 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;
John Meyer
  • 2,296
  • 1
  • 31
  • 39
0

I'm not able to comment divega's post, but among the different solutions presented here, divega's solution produces the best SQL. Both performance wise and length wise. I just checked with SQL Server Profiler and by looking at the execution plan (with "SET STATISTICS PROFILE ON").

Buginator
  • 846
  • 11
  • 18
-2
var result = from entry in table    
             where entry.something == value||entry.something == null                   
              select entry;

use that

Andrew
  • 1
  • 5
    That's VERY wrong because it will select all entries where the value matches AND all entries where something is null, even if you ask for a value. – Michael Stum Oct 31 '11 at 01:54