efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Null Coalesce Operator between two join variable - throws exceptions

Open deadmann opened this issue 3 years ago • 1 comments

Ok, as a sample (Code 1#) I just share part of my code to see how the issue has happened, and why we may need it?

In the following query (Code 1#) as you can see, I used the let operator, which first I thought, hey the issue is for using let as it was previously reported to cause an issue... but later, after inline-ing (Code 2#) the result of the let operator, I noticed the issue is still persists

As you can see I have two join, and I use let to decrease complexity, duplicate null checking and improve the readability of code.

But both codes failed, and unfortunately as we are near the deadline, I'm afraid I cannot provide you stack trace; but the error was not reporting anything meaningful, and the query worked before I start to filter data, so my suggestion is at some point when it fails to bring data, the (x??y).Data will face null reference exception of some kind, but EF handles x.Data ?? y.Data just fine as the last query succeed (Code 3#) and as we do not have the Null Propagation operator (?,) in LINQ queries either it is a bug, or same to DefaultIfEmpty() it just needs a replacement, or the best solution is that Null Coalesce operator (??) work as expected even in such cases.

  1. Sample Query
from person in GetQuery(DbContext.People)
            join personLocalization in DbContext.PersonLocalizations
                on new { person.Id, RequestDataProvider.Culture }
                equals new { Id = personLocalization.LocalizableId, personLocalization.Culture }
                into personLocalizations
            from personLocalization in personLocalizations.DefaultIfEmpty()
            join businessPersonLocalization in DbContext.PersonLocalizations
                on new { person.Id, Culture = RequestDataProvider.MainSetting.DefaultCulture }
                equals new { Id = businessPersonLocalization.LocalizableId, businessPersonLocalization.Culture }
                into businessPersonLocalizations
            from businessPersonLocalization in businessPersonLocalizations.DefaultIfEmpty()

            let currentPersonLocalization = personLocalization ?? businessPersonLocalization
            select new PersonListResponse
            {
                Title = currentPersonLocalization.Title,
            };
  1. Inline-ing the result of let
from person in GetQuery(DbContext.People)
            join personLocalization in DbContext.PersonLocalizations
                on new { person.Id, RequestDataProvider.Culture }
                equals new { Id = personLocalization.LocalizableId, personLocalization.Culture }
                into personLocalizations
            from personLocalization in personLocalizations.DefaultIfEmpty()
            join businessPersonLocalization in DbContext.PersonLocalizations
                on new { person.Id, Culture = RequestDataProvider.MainSetting.DefaultCulture }
                equals new { Id = businessPersonLocalization.LocalizableId, businessPersonLocalization.Culture }
                into businessPersonLocalizations
            from businessPersonLocalization in businessPersonLocalizations.DefaultIfEmpty()

            select new PersonListResponse
            {
                Title = (personLocalization ?? businessPersonLocalization).Title,
            };
  1. Working query
from person in GetQuery(DbContext.People)
            join personLocalization in DbContext.PersonLocalizations
                on new { person.Id, RequestDataProvider.Culture }
                equals new { Id = personLocalization.LocalizableId, personLocalization.Culture }
                into personLocalizations
            from personLocalization in personLocalizations.DefaultIfEmpty()
            join businessPersonLocalization in DbContext.PersonLocalizations
                on new { person.Id, Culture = RequestDataProvider.MainSetting.DefaultCulture }
                equals new { Id = businessPersonLocalization.LocalizableId, businessPersonLocalization.Culture }
                into businessPersonLocalizations
            from businessPersonLocalization in businessPersonLocalizations.DefaultIfEmpty()

            select new PersonListResponse
            {
                Title = personLocalization.Title ?? businessPersonLocalization.Title,
            };

deadmann avatar Feb 07 '22 06:02 deadmann

Can you share a small repro project?

AndriySvyryd avatar Feb 12 '22 02:02 AndriySvyryd