简体   繁体   中英

entity framework can call property on null in expression

I have code to select some objects in the database like this:

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

The question is how does it come that this line works when there are no objects in terms, so the FirstOrDefault() returns null

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

For me this code looks like it could crash while it's actually save.So I was expecting to get a null reference exception in this case. I tried to change the line to

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

That way you know it can be null, but this isn't valid in the expression.

I know it works and doesn't give an exception because the code isn't actually executed but translated to sql, but I'm searching for something explaining why its done like this and not possible in the other way?

Well, like Enigmativity mentioned, the code will not be executed. Because terms is empty no grouping will be done and therefore no FirstOrDefault() will be executed.

You can see it in the following example. (Just a simple one)

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

The code above will be translated into the following one:

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

You can see that your line you mentioned ( ((int?)terms.FirstOrDefault().Deadline ?? 0) ) will result in a NullReferenceException . The ?. will avoid the exception. You could try it here or just create a Console Application with the example code above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM