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.
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>();
}
}
}
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();
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.
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
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).