Skip to content

InvalidOperationException when using constructor parameter in LINQ projection #20502

Closed
@davidkvc

Description

@davidkvc

When creating a projection using IQueryable.Select
I would expect that I can pass arbitrary variable from the context as a parameter to constructed object in the Select projection.
Currently, this throws an exception if I try to do this with the returned object although EF is OK if I do that with some of the inner objects (properties).

Is there any limitation why this does not work that I am missing or is this a bug ?

Steps to reproduce

class TestContext : DbContext
{
    public DbSet<FromModel> Froms { get; set; }
}

class FromModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

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

    public WithParam WithParam { get; set; }

    public ToModel() {}
    public ToModel(object value) {}
}

class WithParam
{
    public string Name { get; }

    public WithParam(object us, string name)
    {
        Name = name;
        //do something with `us`        
    }
}

private static Expression<Func<FromModel, ToModel>> CreateMapperWorking(object value) => 
    //if we passed `value` to ToModel ctor EF Core would throw the exception
    x => new ToModel
    {
        Skey = x.Skey,
        //EF Core has no problem with this constructor parameter being a reference to arbitrary object
        WithParam = new WithParam(value)
    };

private static Expression<Func<FromModel, ToModel>> CreateMapperFailing(object value) => 
    //we have passed the `value` to ToModel ctor so we will get the exception
    x => new ToModel(value)
    {
        Skey = x.Skey,
        //EF Core has no problem with this constructor parameter being a reference to arbitrary object
        WithParam = new WithParam(value)
    };

[Fact]
public async Task ExamplePassingTest()
{
    using var ctx = new TestContext();

    var v = new object();
    _ = await ctx.Froms
        .AsNoTracking()
        .Select(CreateMapperWorking(v))
        .FirstOrDefaultAsync();

    Assert.True(true);
}

[Fact]
public async Task ExampleFailingTest()
{
    using var ctx = new TestContext();

    var v = new object();
    _ = await ctx.Froms
        .AsNoTracking()
        .Select(CreateMapperFailing(v))
        .FirstOrDefaultAsync();

    //above throws System.InvalidOperationException

    Assert.True(true);
}

From the failing test I get

System.InvalidOperationException : Client projection contains reference to constant expression of 'AutoMapperBug.Proof+<>c__DisplayClass10_0'. This could potentially cause memory leak. Consider assigning this constant to local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.
System.InvalidOperationException : Client projection contains reference to constant expression of 'AutoMapperBug.Proof+<>c__DisplayClass10_0'. This could potentially cause memory leak. Consider assigning this constant to local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.ConstantVerifyingExpressionVisitor.VisitConstant(ConstantExpression constantExpression)
   at System.Linq.Expressions.ConstantExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
   at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitNew(NewExpression node)
   at System.Linq.Expressions.NewExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](T node, String callerName)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
   at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor visitor, BlockExpression block)
   at System.Linq.Expressions.ExpressionVisitor.VisitBlock(BlockExpression node)
   at System.Linq.Expressions.BlockExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.InjectEntityMaterializers(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.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__DisplayClass12_0`1.<ExecuteAsync>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.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at AutoMapperBug.Proof.NoAutoMapper2() in C:\Users\d61230\source\repos\Playground\AutoMapperBug\Proof.cs:line 141
--- End of stack trace from previous location where exception was thrown ---

Further technical details

EF Core version: 3.1.1
Database provider: Devart.Data.Oracle.EFCore 9.11.951
Target framework: .NET Core 3.1
Operating system: Windows 10
IDE: Visual Studio 2019 16.5

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions