我想写一个IQueryable扩展方法来过滤掉一些实体。这个方法应该有一个类型为expression_func_lt;T, int_gt;>的形参。用于过滤实体。
这是我的尝试,我不知道为什么这不起作用。什么好主意吗?
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抛出异常:'System. 'Microsoft.EntityFrameworkCore.dll中的InvalidOperationException: ' LINQ表达式'其中(=比;a.删除== False)其中(=比;__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
里面的Contains
里面的Where
不是LINQ到SQL转换器可以处理的东西。正如我注意到的,编译的表达式也不会被翻译成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);
}
这个方法帮助了我,希望它也能帮助你,或者至少能给你指明正确的方向