问题
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 typeint
is never equal tonull
value of typeint?
"
which is true, but in fact I get results perfectly correct, because of my left outer join return null
s 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