问题
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 typeintis never equal tonullvalue of typeint?"
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