2

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?

SᴇM
  • 7,024
  • 3
  • 24
  • 41
  • 1
    Since your whole expression is converted to SQL query and not really executed by .NET framework - that warning doesn't matter, so you can just disable it (for this specific line of course). – Evk Mar 21 '18 at 07:35
  • @Evk yeap, I know that, thanks for answering, I just wanted to be sure that it was correct approach to do so. – SᴇM Mar 21 '18 at 07:37
  • Well you see yourself that it's a false warning - so what could be wrong in disabling it? – Evk Mar 21 '18 at 07:58
  • @Evk Sometimes it's not necessarily "wrong(?)" to do something and there is "more desirable" ways to do that. – SᴇM Mar 21 '18 at 08:06
  • @GertArnold As I said before, in real life example, there is left outer join with 2 columns, for example `C` table stores some `SystemId` and `A_Id`, so you can get which `A_Id`s belong to your system. – SᴇM Mar 21 '18 at 08:38
  • @GertArnold yeap, you are right. – SᴇM Mar 21 '18 at 09:58

3 Answers3

2

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
                    };
Oxald
  • 837
  • 4
  • 10
  • In fact `BoolProp = cc.A_Id != null` is not throwing any exceptions, but `BoolProp = cc != null` makes sense, so I think it'll be more accurate to do so. Thanks. – SᴇM Mar 21 '18 at 08:35
0

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
}
pitaridis
  • 2,801
  • 3
  • 22
  • 41
0

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.

KaeL
  • 3,639
  • 2
  • 28
  • 56