比较不可为 null 的 `int` 和 `null` (LINQ)

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

我有 EF 模型 class,我决定用一个 bool 属性:

扩展 class
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; }
}

然后我写了一个方法,它将 return A 集合与其他 C 集合连接起来,其中包含 A <=> C 映射(好吧,在现实世界中例如它包含一些 SystemId 和 linq 查询将由 2 列连接)和 returns A_DTO 集合:

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 是我的 EF 上下文对象)

当然因为 cc.A_Id 不是可为空的 int 会出现警告消息,说

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

这是真的,但实际上我得到的结果完全正确,因为我的左外连接 return nulls 在 C 集合中缺少映射时。

所以问题是:这样做并保持原样是正确的方法,还是我需要以其他方式实施?

如果 cc.A_Id 永远不会变为 null 并且您想设置 BoolProp 的值,那么您可以通过将 select 更改为以下内容来删除警告:

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

Value Types 将始终具有默认值(将它们声明为 Nullable 时除外),即使它们未初始化,即使您使用的是 LEFT OUTER JOIN 到 [=12] =].因此 A_ID 将始终有一个值,并且 BoolProp 将始终为真。

另一方面,Reference Types 将默认为 NULL

根据DefaultIfEmpty method definition,以下代码片段是等价的:

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

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

因此,当您使用 g.DefaultIfEmpty() 时,您将获得一个唯一的 cc 对象,该对象为空,因此行:

BoolProp = cc.A_Id != null

将抛出 NullReferenceException,因为 cc 为空。

最后条件好像应该是:

BoolProp = cc != null

除此之外,还有一个小例子,它通过单元测试展示了不同之处:

    [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
                    };