Efcore: A combination of downcast and owned types could not be translated in 3.1, but could in 3.0

Created on 3 Dec 2019  路  5Comments  路  Source: dotnet/efcore

When running the supplied code in Efcore 3.0.1, it successfully runs to completion. When running it in Efcore 3.1.0-preview3.19554.8, it throws an InvalidOperationException with the message Could not be translated.

Code to reproduce

using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;

namespace ReproEfcore31CastError
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new AppDbContext())
            {
                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                // THE FOLLOWING EXPRESSION CAUSES THE ERROR
                var thisCausesAnErrorIn31ButNot30 =
                    db
                    .BaseEntities
                    .Select(b =>
                        db.OtherEntities
                        .Where(o => o.OtherEntityData == ((SubEntity)b).Data)
                        .FirstOrDefault()
                    );
                  var errorHappensOnThisLine = thisCausesAnErrorIn31ButNot30.ToArray();
            }
        }
    }

    class BaseEntity
    {
        public int Id { get; set; }
    }

    class SubEntity : BaseEntity
    {
        public string Data { get; set; }
        public Owned Owned { get; set; }
    }


    public class Owned
    {
        public string OwnedData { get; set; }
    }

    class OtherEntity
    {
        public string Id { get; set; }
        public string OtherEntityData { get; set; }
    }

    class AppDbContext : DbContext
    {
        public DbSet<BaseEntity> BaseEntities { get; set; }
        public DbSet<OtherEntity> OtherEntities { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("server=localhost;database=ReproEfCore31CastError;integrated security=true");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<BaseEntity>();
            modelBuilder.Entity<SubEntity>().OwnsOne(se => se.Owned);
            modelBuilder.Entity<OtherEntity>();
        }
    }
}

Code that doesn't produce the error

If the Owned property is removed from SubEntity, it runs fine.
It also works, if the cast is removed, by adding an OfType as follows:

var thisWorks =
    db
    .BaseEntities
    .OfType<SubEntity>()
    .Select(b =>
        db.OtherEntities
        .Where(o => o.OtherEntityData == b.Data)
        .FirstOrDefault()
    )
    .ToArray();

Full error message

The LINQ expression 'DbSet<OtherEntity>
    .Where(o => o.OtherEntityData == ((SubEntity)(IncludeExpression(
        (EntityShaperExpression: 
            EntityType: BaseEntity
            ValueBufferExpression: 
                (ProjectionBindingExpression: EmptyProjectionMember)
            IsNullable: False
        ), 
        (EntityShaperExpression: 
            EntityType: Owned
            ValueBufferExpression: 
                [EntityProjectionExpression]
            IsNullable: True
        ), Owned)
    )).Data)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

Technical details

EF Core version: Efcore 3.1.0-preview3.19554.8 (but not in 3.0.1)
Target Framework: netcoreapp3.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating System: Windows Server 2016 Datacenter 10.0.14393 Build 14393
IDE: Visual Studio Professional 2019 16.3.4

area-query closed-fixed customer-reported type-bug

All 5 comments

Note for triage: stack trace:

Unhandled exception. System.InvalidOperationException: The LINQ expression 'DbSet<OtherEntity>
    .Where(o => o.OtherEntityData == ((SubEntity)(IncludeExpression(
        (EntityShaperExpression:
            EntityType: BaseEntity
            ValueBufferExpression:
                (ProjectionBindingExpression: EmptyProjectionMember)
            IsNullable: False
        ),
        (EntityShaperExpression:
            EntityType: Owned
            ValueBufferExpression:
                [EntityProjectionExpression]
            IsNullable: True
        ), Owned)
    )).Data)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), As
AsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|8_0(ShapedQueryExpression translated, <>c__DisplayClass8_0& )
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.TranslateSubquery(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.Translate(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Program.Main(String[] args) in C:\Stuff\ThreeOne\ThreeOne\Program.cs:line 22

Note for triage: my understanding from triage was that OfType could be a workaround. However, I may have misunderstood since this:

C# var thisCausesAnErrorIn31ButNot30 = db .BaseEntities .OfType<SubEntity>() .Select(b => db.OtherEntities .Where(o => o.OtherEntityData == b.Data) .FirstOrDefault() ); var errorHappensOnThisLine = thisCausesAnErrorIn31ButNot30.ToArray();
returns different results. Specifically, in will filter out any non-SubEntity instances rather than returning null for them.

Scratch that!

Probably combination of other filters/set operations etc can get desired behavior.

Hi,

Thanks for looking into this :-)

I have posted the minimum repro for the issue, which could be worked around. As it stands, the code actually looks sloppy. The expression in our code that uncovered the issue contains a ternary expression, which, while not required to reproduce the error, gives some purpose to the structure of the code.

db
  .BaseEntities
  .Select(b =>
    b is SubEntity
    ? db.OtherEntities
         .Where(o => o.OtherEntityData == ((SubEntity)b).Data)
         .FirstOrDefault()
    : ((SubEntity2)b).OtherEntity 
  );

...

public class OtherSubEntity2 : BaseEntity
{
  public OtherEntity OtherEntity { get; set; }
}

So a workaround using OfType doesn't work for me (unless there is a clever way of using it that I haven't thought of, which is entirely possible).

Was this page helpful?
0 / 5 - 0 ratings