6

I couldn't find the exact words to explain what's happening, so if this is a duplicated question, I apologize.

I tried to do a quite simple AND condition if-clause inside a LINQ Query, in order to check if an object is null and then verify if its property is equal or not the column I wanted to compare.

The code:

public IEnumerable<Plan> GetPlans(Plan plan)
    {
        return _context.Plans.Where(e =>
            e.Situation == plan.Situation &&
            e.Notes.Contains(plan.Notes) &&
            (plan.Excercise != null && plan.Exercise.Year > 0 ? e.Exercise.Year == plan.Exercise.Year: true)).ToList();
    }

I've already done this kind of check a dozen times before in .NET 4.5, without having any kind of issue.

But now, in the first .NET Core 2.0 project I'm working on, I had the following error:

An exception was thrown while attempting to evaluate a LINQ query parameter expression. To show additional information call EnableSensitiveDataLogging() when overriding DbContext.OnConfiguring.

The inner exception is clearer: NULL REFERENCE EXCEPTION.

After some tests, I found out that the error happens when plan.Exercise comes null, even if I try to avoid the exception by checking at first if it's null or not.

If I try to do the same check directly in Immediate Window, it returns "false", as it should be.

Am I missing something here? It could be an EF bug? Any particular reason why this works in .NET 4.5, for example, and not in .NET Core 2.0?

Thanks in advance.

UPDATE

Ivan's solution did the job:

Rewrite ? : constructs with equivalent ||

plan.Excercise == null || plan.Exercise.Year <= 0 || e.Excercise.Year == plan.Exercise.Year
thiagoprzy
  • 411
  • 2
  • 8
  • 21
  • Look into the [null condition operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators) – maccettura Jan 08 '18 at 19:55
  • 1
    Let clarify the terms. Are you asking about EF Core 2.0? If yes, NET 4.5 and NET Core should be irrelevant (EF Core 2.0 does not support NET 4.5). Please update the post and tags accordingly. – Ivan Stoev Jan 08 '18 at 19:57
  • you do check for the plan.Exercise to be null but are you sure plan is always not null? – Dan Dohotaru Jan 08 '18 at 20:00
  • @IvanStoev I edited the question to let it clearer: "I've already done this kind of check a dozen times before in .NET 4.5, without having any kind of issue. But now, in the first .NET Core 2.0 project I'm working on, I had the following error:" – thiagoprzy Jan 08 '18 at 20:03
  • @DanDohotaru yes, I'm 100% percent sure. To be sure about the error I'm having, I added `plan.Exercise = new Exercise()` above the LINQ query and then I had no errors. – thiagoprzy Jan 08 '18 at 20:04
  • My impression is that .Net Core / EF Core isn't ready for production in lots of ways. What happens if you change to only add the final `Where` if `plan.exercise != null`? – NetMage Jan 08 '18 at 20:18
  • 1
    @thiagoprzy You are missing the point. The EF **Core** and it's version are the most important things to know, not NET (Core). Anyway, it's EF Core bug (can't find it right now in their issue tracker). Rewrite `? : ` constructs with equivalent `||`, e.g. `plan.Excercise == null || plan.Exercise.Year <= 0 || e.Excercise.Year == plan.Exercise.Year` – Ivan Stoev Jan 08 '18 at 20:25
  • 1
    @IvanStoev ok, I got your point. I was talking about .NET when the issue should be on EF's behalf. Sorry about that. Your solution did the job. – thiagoprzy Jan 08 '18 at 20:30
  • Side note: the context class contains an incorrect spelling of `Exercise` – Rufus L Jan 08 '18 at 20:39
  • Not sure if it's a bug. The query translator tries to access `plan.Exercise.Year`, in order to translate it into SQL. This amounts to accessing "`null.Year`". In EF6 this wasn't possible either, although the error was different. It would be different if it was `plan.Year (no `Exercise` in-between). Anyway, as said, it's better to add the condition to the query only when it's relevant. – Gert Arnold Jan 08 '18 at 20:50
  • 1
    @Gert They are trying to pre evaluate and eliminate the const parameter expressions (an improvement over EF6 which I was trying to do with expression post processor [here](https://stackoverflow.com/questions/36892232/nullable-object-must-have-a-value-exception-after-checking-for-null-on-a-non-p/36896900#36896900)). They did short circuiting correctly for binary expressions but failed to do so for the conditional operator. – Ivan Stoev Jan 08 '18 at 21:12

3 Answers3

4

It sounds like this might be a bug in EF Core (but I don't know this for sure).

One thing you might try is to fail fast if the base requirements of plan are not met, and more importantly, instead of using the ternary operator, use the traditional comparison operators along with parenthesis:

public IEnumerable<Plan> GetPlans(Plan plan)
{
    if (plan == null) return new List<Plan>();

    return _context.Plans
        .Where(e =>
            e.Situation == plan.Situation &&
            e.Notes.Contains(plan.Notes) &&
            (plan.Exercise == null || 
            plan.Exercise.Year <= 0 || 
            e.Excercise.Year == plan.Exercise.Year))
        .ToList();
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
1

To avoid this issue, make sure that you are not evaluating on null object.

var exercice = await _repositoryExercice.FirstOrDefaultAsync(i => i.IsCurrent);
var depenses = _repositoryDepense.GetAll()
                .Where( e => e.ExerciceId.Equals(exercice.Id))
                .WhereIf(AbpSession.TenantId.HasValue, m => m.TenantId.Value.Equals(AbpSession.TenantId.Value))
                .ToList();

The issue was causing by this line .Where( e => e.ExerciceId.Equals(exercice.Id)) because the variable exercice is null.

Best practice, I replaced that line by this :

...
.WhereIf(exercice != null, e => e.ExerciceId.Equals(exercice.Id))
...
Jhakiz
  • 1,519
  • 9
  • 16
0

how about simplifying your code into something like

public IEnumerable<Plan> GetPlans(int year)
{
    return _context.Plans
        .Where(e => e.Excercise.Year == year)
        .ToList();
}
Dan Dohotaru
  • 2,809
  • 19
  • 15
  • There are more conditions inside the Where clause, I just removed them to not let the code "messy" here. I'll edit the question to let it more obvious. – thiagoprzy Jan 08 '18 at 20:06