Comparing non nullable `int` to `null` (LINQ)

怎甘沉沦 提交于 2021-01-27 17:06:12

问题


I have EF model class and I've decided to extend that class with one bool property:

class A 
{
    public int Id { get; set; }
    public string Value { get; set; }
}

class A_DTO : A
{
    public bool BoolProp { get; set; }
}


class C
{
    public int Id { get; set; }
    public int A_Id { get; set; }
}

Then I wrote a method, which will return that A collection joined with some other C collection, which contains A <=> C mapping (well, in real world example it contains some SystemId and linq query will be joined by 2 columns) and returns A_DTO collection:

internal IQueryable<A_DTO> MyMethod() => 
    from a in dbContext.A
    join c in dbContext.A_C_Mapping
    on a.Id equals c.A_Id into g
    from cc in gg.DefaultIfEmpty()
    select new A_DTO
    {
        Id = a.Id,
        Value = a.Value,
        BoolProp = cc.A_Id != null //<- will be always true, well, that what says warning message
    }

(dbContext is my EF context object)

and of course because of cc.A_Id is not a nullable int the warning message will appear, saying that

"The result of expression will be always 'true' since the value of type int is never equal to null value of type int?"

which is true, but in fact I get results perfectly correct, because of my left outer join return nulls when mapping missing in C collection.

So the question: is it correct approach to do so and leave it as it is, or I need to implement it another way?


回答1:


According to the DefaultIfEmpty method definition, the following code snippets are equivalent:

List<C> list = new List<C>() { }; // empty list
List<C> listDefault = list.DefaultIfEmpty().ToList();

and

List<C> listDefault = new List<C>() { null }; // Since default(C) is null

Thus when you use g.DefaultIfEmpty() you will get a unique cc object which is null, consequently the line:

BoolProp = cc.A_Id != null

will throw a NullReferenceException because cc is null.

At the end it seems that the condition should be:

BoolProp = cc != null

Besides here is a small example which demonstrates the difference through a unit test:

    [TestMethod]
    public void TestMethod_DefaultifEmpty()
    {
        ListA = new List<A>()
        {
            new A { Id=1, Value="111" },
            new A { Id=2, Value="222" },
        };
        ListC = new List<C>()
        {
            new C { Id=1,  A_Id=1 }
        };

        Assert.AreEqual(2, MyMethod().Count());
    }

    public List<A> ListA { get;  set; }
    public List<C> ListC { get; set; }
    public IEnumerable<A_DTO> MyMethod() =>
                    from a in ListA
                    join c in ListC
                    on a.Id equals c.A_Id into g
                    from cc in g.DefaultIfEmpty()
                    select new A_DTO
                    {
                        Id = a.Id,
                        Value = a.Value,
                        //BoolProp = cc.A_Id != null
                        BoolProp = cc != null // replace by previous line to see the difference
                    };



回答2:


If the cc.A_Id is never going to become null and you want to set the value for BoolProp then you can remove the warning by changing your select to be the following:

select new A_DTO
{
    Id = a.Id,
    Value = a.Value,
    BoolProp = true
}



回答3:


Value Types will always have a default value (exception when declaring them as Nullable) even when they are not initalized, even if you are using a LEFT OUTER JOIN through LINQ. Thus A_ID will always have a value, and BoolProp will always be true.

On the other hand, Reference Types will default to NULL.



来源:https://stackoverflow.com/questions/49400009/comparing-non-nullable-int-to-null-linq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!