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

时间:2018-03-21 06:55:07

标签: c# entity-framework linq-to-sql nullable

我有EF模型类,我决定用一个bool属性扩展该类:

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

然后我编写了一个方法,该方法将返回与其他A集合连接的C集合,其中包含A< => C映射(在实际示例中,它包含一些SystemId和linq查询将由2列连接)并返回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,会出现警告信息,说

  

&#34;表达式的结果始终为'true',因为类型int的值永远不会等于null类型的int?值#34;

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

所以问题是:这样做是否正确并保持原样,或者我需要以另一种方式实现它?

3 个答案:

答案 0 :(得分:2)

根据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对象,该对象为null,因此该行:

BoolProp = cc.A_Id != null

将抛出NullReferenceException,因为cc为null。

最后似乎应该是:

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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

Value Types即使您未使用LEFT OUTER JOINNullable,也会始终拥有默认值(将其声明为LINQ时的例外情况),即使它们未被初始化也是如此。因此A_ID将始终具有值,BoolProp将始终为真。

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