[英]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.