Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null Coalesce Operator between two join variable - throws exceptions #27386

Open
deadmann opened this issue Feb 7, 2022 · 1 comment
Open
Assignees
Labels
area-query customer-reported punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-bug
Milestone

Comments

@deadmann
Copy link

deadmann commented Feb 7, 2022

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,
            };
@AndriySvyryd
Copy link
Member

Can you share a small repro project?

@AndriySvyryd AndriySvyryd added this to the 7.0.0 milestone Feb 22, 2022
@ajcvickers ajcvickers added propose-punt punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. and removed propose-punt labels Jul 6, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0, Backlog Jul 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-query customer-reported punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants