efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Contains on concrete ReadOnlyCollection in subquery does not translate

Open ajcvickers opened this issue 1 year ago • 0 comments

Primitive collection declared as a ReadOnlyCollection:

public ReadOnlyCollection<DateOnly> DaysVisited { get; set; }

Query:

    var walksWithADrink = await context.Walks.Select(
        w => new
        {
            Count = w.DaysVisited.Count(v => w.ClosestPub.DaysVisited.Contains(v)),
        }).ToListAsync();

Exception:

Unhandled exception. System.InvalidOperationException: The LINQ expression 'EF.Property<IReadOnlyCollection<DateOnly>>(StructuralTypeShaperExpression: 
    DogWalk
    ValueBufferExpression:
        ProjectionBindingExpression: Outer
    IsNullable: False
, "DaysVisited")
    .AsQueryable()
    .Count(p0 => EF.Property<ReadOnlyCollection<DateOnly>>(StructuralTypeShaperExpression:
        Pub
        ValueBufferExpression:
            ProjectionBindingExpression: Inner
        IsNullable: False
    , "DaysVisited").Contains(p0))' could not be translated. Additional information: Translation of method 'System.Collections.ObjectModel.ReadOnlyCollection<System.DateOnly>.Contains' failed. If this method can be mapped to your custom function, see https://go.microsoft.com/fwlink/?linkid=2132413 for more information. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.TranslateSubquery(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.<VisitMethodCall>g__TranslateAsSubquery|41_0(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateInternal(Expression expression, Boolean applyDefaultTypeMapping)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateProjection(Expression expression, Boolean applyDefaultTypeMapping)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitNew(NewExpression newExpression)
   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 Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query)
   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__DisplayClass11_0`1.<ExecuteCore>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
   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.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in D:\code\AllTogetherNow\One\Program.cs:line 10
   at Program.<Main>(String[] args)

Note that this query works if the primitive collection is IReadOnlyList or IReadOnlyCollection. It only fails when the primitive collection is a concrete ReadOnlyCollection.

Code
using (var context = new SomeDbContext())
{
    await context.Database.EnsureDeletedAsync();
    await context.Database.EnsureCreatedAsync();
   
    var walksWithADrink = await context.Walks.Select(
        w => new
        {
            Count = w.DaysVisited.Count(v => w.ClosestPub.DaysVisited.Contains(v)),
        }).ToListAsync();
}

public class DogWalk
{
    public int Id { get; set; }
    public IReadOnlyCollection<DateOnly> DaysVisited { get; set; }
    public Pub ClosestPub { get; set; } = null!;
}

public class Pub
{
    public int Id { get; set; }
    public ReadOnlyCollection<DateOnly> DaysVisited { get; set; }
}

public class SomeDbContext : DbContext
{
    public DbSet<DogWalk> Walks => Set<DogWalk>();
    public DbSet<Pub> Pubs => Set<Pub>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(
                "Data Source=localhost;Database=BuildBlogs;Integrated Security=True;Trust Server Certificate=True;ConnectRetryCount=0")
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
}

ajcvickers avatar May 02 '24 16:05 ajcvickers