dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.66k stars 3.15k forks source link

'Expression in the SQL tree does not have a type mapping assigned' on ternary with non-null condition and string operation only #34618

Open mycroes opened 1 week ago

mycroes commented 1 week ago

File a bug

When projecting to a string concatenation of a numeric value and a constant string I received an InvalidOperationException stating the `Expression '<EXPRESSION>' in the SQL tree does not have a type mapping assigned. This seems to occur only under the following circumstances (as evidenced by the test cases):

If any of above conditions is not met the issue doesn't occur.

Include your code

// Fails with either SQL LocalDB or actual SQL Server, no other DBs tested

public class Test
{
    // Fails
    [Theory]
    [AutoFakeItEasyData]
    internal void Ternary_Not_Null_Contains(TestDbContext db)
    {
        db.Database.EnsureCreated();
        try
        {
            db.Set<Entity>().Select(x => x != null ? x.Id + "" : null).FirstOrDefault(x => x!.Contains("1"));
        }
        finally
        {
            db.Database.EnsureDeleted();
        }
    }

    // Fails
    [Theory]
    [AutoFakeItEasyData]
    internal void Ternary_Not_Null_StartsWith(TestDbContext db)
    {
        db.Database.EnsureCreated();
        try
        {
            db.Set<Entity>().Select(x => x != null ? x.Id + "" : null).FirstOrDefault(x => x!.StartsWith("1"));
        }
        finally
        {
            db.Database.EnsureDeleted();
        }
    }

    // Pass
    [Theory]
    [AutoFakeItEasyData]
    internal void Ternary_Not_Null_Contains_Non_Numeric_First_Part(TestDbContext db)
    {
        db.Database.EnsureCreated();
        try
        {
            db.Set<Entity>().Select(x => x != null ? "" + x.Id + "" : null).FirstOrDefault(x => x!.Contains("1"));
        }
        finally
        {
            db.Database.EnsureDeleted();
        }
    }

    // Pass
    [Theory]
    [AutoFakeItEasyData]
    internal void Ternary_Not_Null_Equals(TestDbContext db)
    {
        db.Database.EnsureCreated();
        try
        {
            db.Set<Entity>().Select(x => x != null ? x.Id + "" : null).FirstOrDefault(x => x == "1");
        }
        finally
        {
            db.Database.EnsureDeleted();
        }
    }

    // Pass
    [Theory]
    [AutoFakeItEasyData]
    internal void Ternary_Null_Contains(TestDbContext db)
    {
        db.Database.EnsureCreated();
        try
        {
            db.Set<Entity>().Select(x => x == null ? null : x.Id + "").FirstOrDefault(x => x!.Contains("1"));
        }
        finally
        {
            db.Database.EnsureDeleted();
        }
    }

    // Pass
    [Theory]
    [AutoFakeItEasyData]
    internal void Ternary_Null_StartsWith(TestDbContext db)
    {
        db.Database.EnsureCreated();
        try
        {
            db.Set<Entity>().Select(x => x == null ? null : x.Id + "").FirstOrDefault(x => x!.StartsWith("1"));
        }
        finally
        {
            db.Database.EnsureDeleted();
        }
    }

    internal class TestDbContext(DbContextOptions options) : DbContext(options)
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Entity>();

            base.OnModelCreating(modelBuilder);
        }
    }

    private class Entity
    {
        public int Id { get; set; }
    }
}

Include stack traces

Exception only happens in Ternary_Not_Null_Contains and Ternary_Not_Null_StartsWith, stack trace seems identical so this is only the trace from Ternary_Not_Null_Contains.

System.InvalidOperationException
Expression 'CASE
    WHEN Not(e.Id == NULL) THEN Convert(e.Id) + 
    ELSE NULL
END' in the SQL tree does not have a type mapping assigned.
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.RelationalInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerQueryableMethodTranslatingExpressionVisitor.SqlServerInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.LikeExpression.VisitChildren(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.RelationalInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerQueryableMethodTranslatingExpressionVisitor.SqlServerInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.VisitChildren(ExpressionVisitor visitor, Boolean updateColumns)
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.VisitChildren(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.RelationalInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerQueryableMethodTranslatingExpressionVisitor.SqlServerInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.RelationalInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerQueryableMethodTranslatingExpressionVisitor.SqlServerInferredTypeMappingApplier.VisitExtension(Expression expression)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerQueryableMethodTranslatingExpressionVisitor.ApplyInferredTypeMappings(Expression expression, IReadOnlyDictionary`2 inferredTypeMappings)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)
   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.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 <redacted>.Test.Ternary_Not_Null_Contains(TestDbContext db) in <redacted>\EFCoreProjection.cs:line 14
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)

Include verbose output

NA

Include provider and version information

EF Core version: 8.0.8 Database provider: Microsoft.EntityFrameworkCore.SqlServer, 8.0.8 Target framework: .NET 8.0 Operating system: Windows 10 (10.0.19045.4780) IDE: Visual Studio 2022 17.10.3

ChrisJollyAU commented 1 week ago

Think I have a fix for this, must just sort out the tests