Home > Software engineering >  LINQ query throws client side evaluation error
LINQ query throws client side evaluation error

Time:04-23

Been recently delving into EF Core 6 and I'm getting a client side evalutation error which I'm wondering if there's any way to rewrite this in a more efficient way.

I need to query some Photos based on a list of TagIds. For example when filtering by the tags [Portugal,Beach], need to get all photos that include all those tags. So the photo X with [Portugal, Beach, Food] will be included but the photo Y [Portugal] will be out.

I believe I need to achieve something like this but that throws the client side evaluation exception:

_context.Photo
    .Include(i => i.PhotoTags)
       .ThenInclude(j => j.Tag)
    .Where(i => filter.tagIds.All(j => i.PhotoTags.Exists(k => k.TagId == j)))

With the following classes:

public class Photo
{
    public Guid Id { get; set; }
        
    public string Description { get; set; }

    public DateTime Date { get; set; }

    public bool IsCover { get; set; }
        
    public virtual List<PhotoTag> PhotoTags { get; set; }
        
}

public class PhotoTag : Base
{
    public Guid Id { get; set; }

    public Tag Tag { get; set; }
    
    public Guid TagId { get; set; }
    
    public Photo Photo { get; set; }
    
    public Guid PhotoId { get; set; }
}

Edit

This is the error I'm getting:

System.InvalidOperationException: The LINQ expression 'j => MaterializeCollectionNavigation(
    Navigation: Photo.PhotoTags,
    subquery: DbSet<PhotoTag>()
        .Where(p0 => EF.Property<Guid?>(EntityShaperExpression: 
            rvc.Models.Photo
            ValueBufferExpression: 
                ProjectionBindingExpression: EmptyProjectionMember
            IsNullable: False
        , "Id") != null && object.Equals(
            objA: (object)EF.Property<Guid?>(EntityShaperExpression: 
                rvc.Models.Photo
                ValueBufferExpression: 
                    ProjectionBindingExpression: EmptyProjectionMember
                IsNullable: False
            , "Id"), 
            objB: (object)EF.Property<Guid?>(p0, "PhotoId")))).Exists(k => k.TagId == j)' 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 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitLambda[T](Expression`1 lambdaExpression)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   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.TranslateInternal(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.Translate(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateExpression(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateLambdaExpression(ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateWhere(ShapedQueryExpression source, LambdaExpression predicate)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.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.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.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at rvc.Services.PhotoService.GetAll(FilterParameters filter) in C:\Users\jjtfs\Work\rvc\server\Services\PhotoService.cs:line 145

CodePudding user response:

This can be rewritten in a translatable form by counting the tags that are in the filter. This count should equal the number of items in the filter:

var count = filter.tagIds.Count();

var result = _context.Photo
.Include(p => p.PhotoTags)
    .ThenInclude(pt => pt.Tag)
.Where(p => p.PhotoTags.Count(pt => filter.tagIds.Contains(pt.TagId)) == count);
  • Related