我想写一个IQueryable的扩展方法来过滤掉一些实体。此方法应具有类型为 Expression
这是我的尝试,我不知道为什么这不起作用。有什么想法吗?
public static IQueryable<T> AddDefaultQuery<T>(this IQueryable<T> query, Expression<Func<T, int>> countryId, ApplicationDbContext db, ClaimsPrincipal claimsPrincipal)
{
if (claimsPrincipal.IsEppRole()) {
var userCountryIds = db.UserCountry.Where(uc => uc.UserId == LoginUser.GetUserID(claimsPrincipal)).Select(uc => uc.CountryId);
query = query.Where(q => userCountryIds.Contains(countryId.Compile()(q)));
}
return query;
}
错误是:
发生异常:CLR/System.InvalidOperationException 抛出的异常:Microsoft.EntityFrameworkCore.dll 中的“System.InvalidOperationException”:“LINQ 表达式”DbSet() .Where(a => a.Deleted == False) .Where(a => __userCountryIds_0.Contains(__Compile_1.Invoke(a)))' 无法翻译。以可以翻译的形式重写查询,或者通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用来显式切换到客户端评估。有关详细信息,请参阅 https://go.microsoft.com/fwlink/?linkid=2101038。'
最近有同样的问题。问题出在这里:
.Where(q => userCountryIds.Contains(countryId.Compile()(q)))
我了解到
Func
inside Contains
inside Where
并不是 LINQ to SQL 转换器可以处理的。正如我所注意到的,compiled 表达式也没有转换为 SQL,它们将在查询 SQL 后执行,这可能会导致性能下降。
我猜你有这样的表情
Expression<Func<T, int>> expression = foo => foo.Id
现在如果我们创建一个这样的表达式:
//don't forget to put a constraint on T like
//"where T : IHasId" or something like that
public static Expression<Func<T, bool>> BuildExpression<T>(ApplicationDbContext db, ClaimsPrincipal claimsPrincipal)
{
var userCountryIds = db.UserCountry
//this one might also not be translated to SQL
//because it uses function that is not supported by SQL
.Where(uc => uc.UserId == LoginUser.GetUserID(claimsPrincipal))
.Select(uc => uc.CountryId)
.ToList();
var isEppRole = claimsPrincipal.IsEppRole();
return foo => !isEppRole || userCountryIds.Contains(foo.Id);
}
然后在你的扩展方法中
public IQueryable<T> AddDefaultQuery<T>(this IQueryable<T> query, ApplicationDbContext db, ClaimsPrincipal claimsPrincipal)
{
var exp = BuildExpression<T>(db, claimsPrincipal);
//no lambdas, nothing else except the expression itself
return query.Where(exp);
}
这种方法对我有帮助,希望它对你也有帮助,或者至少会为你指明正确的方向