[英]How to create a reusable 'Contains' expression for EF Core
我需要通过使用表达式的通用存储库与其他过滤器一起执行部分文本搜索。
我有一个通用方法,它从我的数据库返回分页结果(通过公共存储库层)。
在以下工作示例中;
PagedRequest
包含当前页面大小和页码,并在各自的Skip
/ Take
操作期间使用。PagedResult
包含结果的集合以及记录总数。public Task<PagedResult<Person>> GetPeopleAsync(PersonSearchParams searchParams,
PagedRequest pagedRequest = null)
{
ParameterExpression argParam = Expression.Parameter(typeof(Locum), "locum");
// start with a "true" expression so we have an expression to "AndAlso" with
var alwaysTrue = Expression.Constant(true);
var expr = Expression.Equal(alwaysTrue, alwaysTrue);
if (searchParams != null)
{
BinaryExpression propExpr;
if (searchParams.DateOfBirth.HasValue)
{
propExpr = GetExpression(searchParams.DateStart,
nameof(Incident.IncidentDate),
argParam,
ExpressionType.GreaterThanOrEqual);
expr = Expression.AndAlso(expr, propExpr);
}
if (searchParams.DateOfDeath.HasValue)
{
propExpr = GetExpression(searchParams.DateEnd,
nameof(Incident.IncidentDate),
argParam,
ExpressionType.LessThanOrEqual);
expr = Expression.AndAlso(expr, propExpr);
}
if (searchParams.BranchId.HasValue && searchParams.BranchId.Value != 0)
{
propExpr = GetExpression(searchParams.BranchId,
nameof(Incident.BranchId), argParam);
expr = Expression.AndAlso(expr, propExpr);
}
}
var lambda = Expression.Lambda<Func<Locum, bool>>(expr, argParam);
return _unitOfWork.Repository.GetAsync(filter: lambda, pagedRequest: pagedRequest);
}
这是对Expression.Equal
、 Expression.GreaterThanOrEqual
和Expression.LessThanOrEqual
查询使用我的 static GetExpression
方法,如下所示;
private static BinaryExpression GetExpression<TValue>(TValue value,
string propName, ParameterExpression argParam, ExpressionType? exprType = null)
{
BinaryExpression propExpr;
var prop = Expression.Property(argParam, propName);
var valueConst = Expression.Constant(value, typeof(TValue));
switch (exprType)
{
case ExpressionType.GreaterThanOrEqual:
propExpr = Expression.GreaterThanOrEqual(prop, valueConst);
break;
case ExpressionType.LessThanOrEqual:
propExpr = Expression.LessThanOrEqual(prop, valueConst);
break;
case ExpressionType.Equal:
default:// assume equality
propExpr = Expression.Equal(prop, valueConst);
break;
}
return propExpr;
}
注意:此代码工作正常。
使用其他 SO 答案中的示例,我尝试了以下方法;
我尝试通过Expression
获取包含;
static Expression<Func<bool>> GetContainsExpression<T>(string propertyName,
string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return Expression.Lambda<Func<bool>>(containsMethodExp);
}
这必须转换为BinaryExpression
,以便可以使用AndAlso
将其添加到表达式树中。 我试图将Expression
与true
值进行比较,但这不起作用
if (searchParams.FirstName.IsNotNullOrWhiteSpace())
{
var propExpr = GetContainsExpression<Locum>(nameof(Locum.Firstname),
searchParams.FirstName);
var binExpr = Expression.MakeBinary(ExpressionType.Equal, propExpr, propExpr);
expr = Expression.AndAlso(expr, binExpr);
}
我还尝试使用以下方法返回MethodCallExpression
(而不是上面的 Lambda);
static MethodCallExpression GetContainsMethodCallExpression<T>(string propertyName,
string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return containsMethodExp;
}
我按如下方式使用它;
if (searchParams.FirstName.IsNotNullOrWhiteSpace())
{
var propExpr = GetContainsMethodCallExpression<Person>(nameof(Person.FirstName),
searchParams.FirstName);
var binExpr = Expression.MakeBinary(ExpressionType.Equal, propExpr, alwaysTrue);
expr = Expression.AndAlso(expr, binExpr);
}
这些表达式被传递给从数据库中分页信息的通用方法,并且在查询的第一次执行期间,当 I Count the total matching number of Record on theConstructed query
时抛出异常。
System.InvalidOperationException: 'LINQ 表达式'DbSet().Where(p => True && p.FirstName.Contains("123") == True)' 无法翻译。 以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用显式切换到客户端评估。 有关详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=2101038 。
我在分页代码中使用的Count
方法引发了此异常。 此代码已经在没有任何过滤器的情况下工作,并且使用顶部描述的ExpressionType
过滤器,因此我没有包含此代码,因为我认为它不相关。
pagedResult.RowCount = query.Count();
这必须转换为 BinaryExpression,以便可以使用
AndAlso
将其添加到表达式树中
消极的。 不要求Expression.AndAlso
(或Expression.OrElse
)操作数是二进制表达式(如果要求&&
或||
的左或右操作数始终是比较运算符,那会很奇怪)。 唯一的要求是它们是bool
返回表达式,因此对字符串Contains
的调用是一个完全有效的操作数表达式。
因此,首先将内部局部变量的类型从BinaryExpression
更改为Expression
:
if (searchParams != null)
{
Expression propExpr;
// ...
}
同样的 btw 适用于初始表达式 - 你不需要true == true
,简单的Expression expr = Expression.Constant(true);
也会这样做。
现在您可以在一个单独的方法中发出对string.Contains
的方法调用,该方法类似于您发布的另一个方法(传递ParameterExpression
和构建属性选择器表达式)或类似于以下内容的内联:
if (searchParams.FirstName.IsNotNullOrWhiteSpace())
{
var propExpr = Expression.Property(argParam, nameof(Person.FirstName));
var valueExpr = Expression.Constant(searchParams.FirstName);
var containsExpr = Expression.Call(
propExpr, nameof(string.Contains), Type.EmptyTypes, valueExpr);
expr = Expression.AndAlso(expr, containsExpr);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.