LINQ 查询引发客户端评估错误

LINQ query throws client side evaluation error

最近一直在研究 EF Core 6,我遇到了一个客户端评估错误,我想知道是否有任何方法可以更有效地重写它。

我需要根据 TagIds 的列表查询一些 Photos。例如,当按标签 [Portugal,Beach] 过滤时,需要获取包含 所有这些标签 的所有照片。所以带有 [葡萄牙,海滩,美食] 的照片 X 将被包括在内,但照片 Y [葡萄牙] 将被排除在外。

我相信我需要实现这样的目标,但这会引发客户端评估异常:

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

与以下 类:

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; }
}

编辑

这是我遇到的错误:

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

这可以通过计算过滤器中的标签以可翻译的形式重写。此计数应等于过滤器中的项目数:

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);