question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Query: invalid SQL produced for queries with navigation inside predicate of SelectMany-Where-DefaultIfEmpty() pattern

See original GitHub issue

So I have this query with two LEFT JOINS. Everything worked as expected when I had only one LEFT JOIN.

var dbQuery = from i in context.Ingredients
              from ii in i.CombinationIngredients.Where(ci => ci.IngredientId == forIngredientId).DefaultIfEmpty()
              select new
              {
                  i.Id,
                  i.Name,
                  Linked = ii != null
              };

However, when I add a second LEFT JOIN I see multiple queries in the database and I get an Exception.

var dbQuery = from i in context.Ingredients
              from ii in i.CombinationIngredients.Where(ci => ci.IngredientId == forIngredientId).DefaultIfEmpty()
              from t in i.Translations.Where(l => l.Language.Iso639_1 == language).DefaultIfEmpty()
              select new
              {
                  i.Id,
                  i.Name,
                  t.Translation,
                  Linked = ii != null
              };

I add a Skip(…).Take(100) elsewhere in my code, but one of the queries is a “SELECT [all columns] FROM Ingredient” making this a HUGE performance hit! But, what’s even worse is that it doesn’t work AT ALL.

Notice how I added t.Translation to the return value? I get the error “System.Data.SqlClient.SqlException: ‘Invalid column name ‘Translation’.’” EF somehow figures out that the column Translation is part of the Language table, while it’s part of the IngredientTranslation table (which is what i.Translations points to). One of the generated queries:

SELECT [i.Translations_groupItem.Language0].[Id], [i.Translations_groupItem.Language0].[Iso639_1], [i.Translations_groupItem.Language0].[Translation]
FROM [Language] AS [i.Translations_groupItem.Language0]

I do have a workaround, which is to just write the full join.

var dbQuery = from i in context.Ingredients
              from ii in i.CombinationIngredients.Where(ci => ci.IngredientId == forIngredientId).DefaultIfEmpty()
              join tran in context.IngredientTranslations.Where(l => l.Language.Iso639_1 == language) on i.Id equals tran.IngredientId into tranGroup
              from t in tranGroup.DefaultIfEmpty()
              select new
              {
                  i.Id,
                  i.Name,
                  t.Translation,
                  Linked = ii != null
              };

I’d prefer to use the much shorter “from x in …” syntax though. I know this works in the non-core EF version.

Further technical details

EF Core version: 2.1.0-preview1-final Database Provider: Microsoft.EntityFrameworkCore.SqlServer Operating system: Windows 10 IDE: Visual Studio 2017 15.6.7

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:10 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
maumarcommented, May 1, 2018

EFCore was written from scratch so it usually doesn’t share issues with EF6. We will definitely fix this - navigation inside predicate of the LEFT JOIN pattern is a compelling scenario so it should work. However at this point we only accept critical issues and regressions for the 2.1 release, so the fix will most likely land after 2.1 has shipped.

1reaction
maumarcommented, Apr 30, 2018

Simplified repro:

    class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new MyContext())
            {
                ctx.Database.EnsureDeleted();
                ctx.Database.EnsureCreated();

                var apple = new Ingredient { Active = true, Name = "Apple" };
                var pear = new Ingredient { Active = true, Name = "Pear" };
                var dutch = new Language { Active = true, Iso639_1 = "nl", Name = "Dutch" };
                var english = new Language { Active = true, Iso639_1 = "en", Name = "English" };
                ctx.Add(apple);
                ctx.Add(pear);
                ctx.Add(dutch);
                ctx.Add(english);
                ctx.Add(new IngredientTranslation { Ingredient = apple, Language = dutch, Translation = "Appel" });
                ctx.Add(new IngredientTranslation { Ingredient = pear, Language = dutch, Translation = "Peer" });
                ctx.SaveChanges();
            }

            using (var ctx = new MyContext())
            {
                string language = "nl";

                var query = from i in ctx.Ingredients
                            from t in i.Translations.Where(l => l.Language.Iso639_1 == language).DefaultIfEmpty()
                            select new
                              {
                                  i.Id,
                                  i.Name,
                                  t.Translation,
                              };

                var result = query.ToList();
            }
        }
    }

    public class MyContext : DbContext
    {
        public virtual DbSet<Ingredient> Ingredients { get; set; }
        public virtual DbSet<IngredientTranslation> IngredientTranslations { get; set; }
        public virtual DbSet<Language> Languages { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=.;Database=Repro11847;Trusted_Connection=True;MultipleActiveResultSets=True");
        }
    }

    public class Ingredient
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool Active { get; set; }

        public virtual ICollection<IngredientTranslation> Translations { get; set; }
    }

    public class IngredientTranslation
    {
        public int Id { get; set; }
        public string Translation { get; set; }

        public int IngredientId { get; set; }
        public Ingredient Ingredient { get; set; }

        public int LanguageId { get; set; }
        public Language Language { get; set; }
    }

    public class Language
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool Active { get; set; }
        public string Iso639_1 { get; set; }
    }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Query: invalid SQL produced for queries with navigation ...
Query : invalid SQL produced for queries with navigation inside predicate of SelectMany-Where-DefaultIfEmpty() pattern #11847.
Read more >
Invalid SQL Error - OutSystems 11 Documentation
You are using Foreign Entities in the query that are located in different databases. Each SQL query can only use Foreign Entities located...
Read more >
Overview of Predicates | InterSystems SQL Reference
A predicate is a condition expression that evaluates to a boolean value, either true or false. Predicates can be used as follows: In...
Read more >
Double range query on one column uses wrong seek ...
I am using SQL Server 2017. Given a table with only one non-id column, which takes only one value: create table #test (id ......
Read more >
invalid escape character "\\" was specified in a LIKE predicate
i'm getting this error message with my query.. i'm using openjpa and sql server for my database... this is my query: public static...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found