我首先使用EntityFramework
数据库使用REST WebAPI。所有代码都是从EDMX
文件,实体,存储库类和API控制器等生成的。
我添加了一些过滤功能,允许用户通过查询字符串添加条件,该字符串转换为LinqKit PredicateBuilder / Linq
表达式,用于在命中数据库时过滤结果。
e.g. /api/Users?FirstName_contains=Rog
这将返回User.FirstName
成员中具有“Rog”的所有用户。这使用PredicateBuilder
动态构建适当的Linq
表达式,然后将其用作Where
的{{1}}子句。
例如:
DbSet
现在问题。我希望客户能够根据成员的组合来匹配结果。
var fieldName = "FirstName";
var value = "Rog";
var stringContainsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var parameter = Expression.Parameter(typeof(User), "m");
var fieldAccess = Expression.PropertyOrField(parameter, fieldName);
var fieldType = typeof(User).GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public).PropertyType;
var expression = Expression.Lambda<Func<User, bool>>(Expression.Call(fieldAccess, stringContainsMethod, Expression.Constant(value, fieldType))
, parameter)
var andPredicate = PredicateBuilder.True<User>();
andPredicate = andPredicate.And(expression);
var query = Db.Users
.AsQueryable()
.AsExpandable()
.Where(andPredicate);
即。搜索e.g. /api/Users?api_search[FirstName,LastName]=Rog
搜索'Rog'的匹配项,这样我就可以搜索'Roger Sm'并获得名字= Roger和姓氏= Smith的结果。
如果我使用流利的方式查询first name + last name
,它将会是:
DbSet
我正在努力的是创建一个users.Where(u => (u.FirstName + " " + u.LastName).Contains("Rog"));
表达式,它将动态处理字符串成员predicate / linq
的串联。
答案 0 :(得分:1)
这里并不真正需要PredicateBuilder。
字符串连接表达式可以使用EF支持的string.Concat
方法调用生成:
static Expression<Func<T, string>> GenerateConcat<T>(IEnumerable<string> propertyNames)
{
var parameter = Expression.Parameter(typeof(T), "e");
// string.Concat(params string[] values)
var separator = Expression.Constant(" ");
var concatArgs = Expression.NewArrayInit(typeof(string), propertyNames
.SelectMany(name => new Expression[] { separator, Expression.PropertyOrField(parameter, name) })
.Skip(1));
var concatCall = Expression.Call(typeof(string).GetMethod("Concat", new[] { typeof(string[]) }), concatArgs);
return Expression.Lambda<Func<T, string>>(concatCall, parameter);
}
字符串包含谓词可以通过简单的string.Contains
方法调用生成:
static Expression<Func<T, bool>> GenerateContains<T>(Expression<Func<T, string>> member, string value)
{
var containsCall = Expression.Call(member.Body, "Contains", Type.EmptyTypes, Expression.Constant(value));
return Expression.Lambda<Func<T, bool>>(containsCall, member.Parameters);
}
将它们与您的示例结合起来:
var predicate = GenerateContains(GenerateConcat<User>(new[] { "FirstName", "LastName" }), "Rog");
答案 1 :(得分:0)
尝试以下操作(我没有针对数据库进行测试):
public class User
{
public string FirstName { get; set; }
public string LastName { get; set;}
}
void Main()
{
List<User> users = new List<User> {
new User { FirstName = "john", LastName = "smith" },
new User { FirstName = "siler", LastName = "johnston" } };
string searchName = "ja smi";
String[] terms = searchName.Split(' ');
var items = users.Where(x => terms.Any(y => x.FirstName.Contains(y))
|| terms.Any(y => x.LastName.Contains(y)));
}