【问题标题】:entity framework can call property on null in expression实体框架可以在表达式中调用 null 属性
【发布时间】:2018-04-18 13:31:28
【问题描述】:

我有这样的代码来选择数据库中的一些对象:

db.ObjectTypes.GroupJoin(db.Terms, t => t.Id, term => term.ObjectTypeId, (t, terms) => new Term()
{
    Type = t.Type,
    ObjectTypeId = t.Id,
    Deadline = ((int?)terms.FirstOrDefault().Deadline ?? 0),
});

问题是当没有对象时,这条线是如何工作的,所以FirstOrDefault() 返回null

((int?)terms.FirstOrDefault().Deadline ?? 0)

对我来说,这段代码看起来在实际保存时可能会崩溃。所以我期待在这种情况下得到一个空引用异常。我试图将行更改为

(terms.FirstOrDefault()?.Deadline ?? 0)

这样你就知道它可以为空,但这在表达式中无效。

我知道它可以工作并且没有给出异常,因为代码实际上并没有被执行而是被翻译成 sql,但我正在寻找一些解释为什么它这样做而不能以其他方式完成的东西?

【问题讨论】:

  • 提示代码没有执行。它被转换为 SQL,如果值为 null,则不会调用该属性。
  • @Enigmativity 我知道它已被转换,但它仍然看起来像无效代码,而看起来有效的东西不被接受。
  • 不支持“另一种方式”不是 EF 错误。首先,在开发 EF 时,?. 运算符并不存在。第二个也是最重要的,正如您所注意到的,C# 表达式树不支持运算符 ?.(因为 MS 开发人员没有明确的想法,也没有决定应该用什么类型的(新)表达式来表示它),因此 EF 和其他表达式翻译器无能为力。

标签: c# linq-to-sql entity-framework-6 nullreferenceexception linq-expressions


【解决方案1】:

好吧,就像 Enigmativity 提到的,代码不会被执行。因为terms 为空,所以不会进行分组,因此不会执行FirstOrDefault()

您可以在以下示例中看到它。 (只是一个简单的)

using System;
using System.Collections.Generic;
using System.Linq;

public class static Program {

    public static void Main() 
    {
        var terms = new List<Term>();

        var result = ((int?)terms.FirstOrDefault().Deadline ?? 0);        
        Console.WriteLine(result);

        var result2 = (terms.FirstOrDefault()?.Deadline ?? 0);
        Console.WriteLine(result2);
    }

    private class Term
    {
        public int Deadline { get; set; }
    }
}

上面的代码会被翻译成下面的代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;

[assembly: AssemblyVersion("0.0.0.0")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[module: UnverifiableCode]
public class Program
{
    private class Term
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
        private int <Deadline>k__BackingField;

        public int Deadline
        {
            [CompilerGenerated]
            get
            {
                return this.<Deadline>k__BackingField;
            }
            [CompilerGenerated]
            set
            {
                this.<Deadline>k__BackingField = value;
            }
        }
    }

    public static void Main()
    {
        List<C.Term> source = new List<C.Term>();
        int? num = new int?(source.FirstOrDefault<C.Term>().Deadline);
        int value = num.HasValue ? num.GetValueOrDefault() : 0;
        Console.WriteLine(value);

        // just added this line for more readability
        C.Term expr_3A = source.FirstOrDefault<C.Term>();
        int value2 = (expr_3A != null) ? expr_3A.Deadline : 0;
        Console.WriteLine(value2);
    }
}

您可以看到您提到的行 (((int?)terms.FirstOrDefault().Deadline ?? 0)) 将生成 NullReferenceException?. 将避免异常。 你可以试试here 或者使用上面的示例代码创建一个Console Application

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-21
    相关资源
    最近更新 更多