我需要在表达式中使用SqlFunctions.PatIndex。
Func<IQueryable<T>, KendoFilterDescription, IQueryable<T>> appendFilter =
(filteredData, filter) => filteredData.Where(String.Format("System.Data.Objects.SqlClient.SqlFunctions.PatIndex(\"@0\", {0})", filter.Field), ParsePropertyValue(filter));
但我得到一个例外:
No property or field 'System' exists in type 'RecordListItem'
如何使用此功能?
最佳答案
由于您的代码示例中的表达式很长,因此可能很难看到它,但是看起来“ Where”子句包含错误,因为它不是一个可以简化为Func<T, bool>
的表达式。
我假设这就是为什么您遇到编译器错误。如果没有关于KendoFilterDescription
是什么,ParsePropertyValue
的功能以及您尝试使用PatIndex
进行过滤的信息的更多信息,很难为您找到一个好的答案。
因此,现在我将尝试一个疯狂的猜测:
您正在尝试使用PatIndex表达式过滤IQueryable。
如果评估的PatIndex表达式未返回0,则“ Where”的评估结果应为true,因此记录将包括在结果中。
您希望PatIndex表达式的stringPattern
参数等于ParsePropertyValue(filter)
的返回值,即要匹配的模式。
您希望PatIndex表达式的target
参数等于名称为T
的filter.Field
属性或字段的值。
因此,以一个示例为例,我们使用具有FirstName和LastName属性的Person(T)类。并假设您希望过滤包含某个短语的FirstName,那么您将希望构建一个类似以下的表达式:
Func<string, string> BuildContainsPattern = phrase => string.Format("%{0}%", phrase);
Func<IQueryable<Person>, string, IQueryable<Person>> whereFirstNameContains =
(data, phrase) => data.Where(p => SqlFunctions.PatIndex(BuildContainsPattern(phrase), p.FirstName) > 0);
假设这是(基本上)正确的。我们需要一些构建块,这些构建块一起使我们能够构建可以传递给
Expression<Func<T, bool>>
的Where
子句的IQueryable<T>
。好的,那么困难的部分在第一。下面的
BuildFilterExpression
函数可以为我们构建此表达式,只要我们为其提供(1)预期作为stringPattern
函数的第一个参数的PatIndex
和(2)实体上属性的名称键入我们希望用作T
函数的第二个参数的PatIndex
。因此,如果我对
ParsePropertyValue(filter)
和filter.Field
表示的假设是正确的,则下面的代码应使用过滤器提供的设置进行过滤。using System;
using System.Linq;
using System.Reflection;
using System.Data.Objects.SqlClient;
using System.Linq.Expressions;
public class FilterDescription
{
public enum FilterPatternType
{
Contains = 1,
Range = 2, // [^0-9]
}
public string Field { get; set; }
public string FilterPhrase { get; set; }
public FilterPatternType PatternType { get; set; }
}
public static class FilterBuilder
{
private static readonly MethodInfo PatIndexMethod = typeof(SqlFunctions).GetMethod("PatIndex");
private static readonly ConstantExpression ValueZero = Expression.Constant(0, typeof(int?));
public static string ParsePropertyValue(FilterDescription filter)
{
switch (filter.PatternType)
{
case FilterDescription.FilterPatternType.Contains:
return string.Format("%{0}%", filter.FilterPhrase);
case FilterDescription.FilterPatternType.Range:
return string.Format("[^{0}]", filter.FilterPhrase);
default:
throw new InvalidOperationException("Pattern type not supported");
}
}
public static Expression<Func<TEntity, bool>> BuildFilterExpression<TEntity>(string patternString, string targetProperty)
{
var patternStringArg = Expression.Constant(patternString);
var entityType = Expression.Parameter(typeof(TEntity), "item");
var targetPropertyArg = Expression.PropertyOrField(entityType, targetProperty);
MethodCallExpression patIndexCall = Expression.Call(PatIndexMethod, patternStringArg, targetPropertyArg);
var isGreaterThanZero = Expression.GreaterThan(patIndexCall, ValueZero);
return Expression.Lambda<Func<TEntity, bool>>(isGreaterThanZero, entityType);
}
public static Expression<Func<TEntity, bool>> BuildFilterExpression<TEntity>(FilterDescription filter)
{
var pattern = ParsePropertyValue(filter);
return BuildFilterExpression<TEntity>(pattern, filter.Field);
}
public static IQueryable<TEntity> Filter<TEntity>(this IQueryable<TEntity> toFilter, FilterDescription filter)
{
return toFilter.Where(BuildFilterExpression<TEntity>(filter));
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
class Program
{
private static IQueryable<Person> GetPersons()
{
return (IQueryable<Person>)null; // use your own data.
}
public static void Main(params string[] args)
{
var filter = new FilterDescription()
{
PatternType = FilterDescription.FilterPatternType.Contains,
Field = "FirstName",
FilterPhrase = "ed"
};
var filtered = GetPersons().Filter(filter);
}
}
显然,我完全有可能完全误以为是错误的,但是我想您会改善对要达到的目标的描述。