Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Linq PredicateBuilder连接字符串成员以进行文本搜索_C#_Entity Framework_Linq_Predicatebuilder - Fatal编程技术网

C# 使用Linq PredicateBuilder连接字符串成员以进行文本搜索

C# 使用Linq PredicateBuilder连接字符串成员以进行文本搜索,c#,entity-framework,linq,predicatebuilder,C#,Entity Framework,Linq,Predicatebuilder,我有一个RESTWebAPI,首先使用EntityFramework数据库。所有代码都是从EDMX文件、实体、存储库类和API控制器等生成的 我添加了一些过滤功能,允许用户通过查询字符串添加条件,这些查询字符串转换为LinqKit-PredicateBuilder/Linq表达式,在命中数据库时过滤结果 e.g. /api/Users?FirstName_contains=Rog 这将返回User.FirstName成员中带有“Rog”的所有用户。这使用PredicateBuilder动态构建

我有一个RESTWebAPI,首先使用
EntityFramework
数据库。所有代码都是从
EDMX
文件、实体、存储库类和API控制器等生成的

我添加了一些过滤功能,允许用户通过查询字符串添加条件,这些查询字符串转换为
LinqKit-PredicateBuilder/Linq
表达式,在命中数据库时过滤结果

e.g. /api/Users?FirstName_contains=Rog
这将返回
User.FirstName
成员中带有“Rog”的所有用户。这使用
PredicateBuilder
动态构建适当的
Linq
表达式,然后作为
Where
子句用于
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);
var predicate = GenerateContains(GenerateConcat<User>(new[] { "FirstName", "LastName" }), "Rog");
i、 e.搜索
first name+last name
中的“Rog”匹配项,这样我就可以搜索“Roger Sm”,并得到first name=Roger和last name=Smith的结果

如果我要使用fluent查询
DbSet
,它将如下所示:

users.Where(u => (u.FirstName + " " + u.LastName).Contains("Rog"));
我正在努力创建一个
谓词/linq
表达式,该表达式将动态处理字符串成员的串联
FirstName+“”+LastName

尝试以下操作(我还没有针对数据库进行测试):

公共类用户
{
公共字符串名{get;set;}
公共字符串LastName{get;set;}
}
void Main()
{
列表用户=新列表{
新用户{FirstName=“john”,LastName=“smith”},
新用户{FirstName=“siler”,LastName=“johnston”};
string searchName=“ja smi”;
String[]terms=searchName.Split(“”);
var items=users.Where(x=>terms.Any(y=>x.FirstName.Contains(y))
||任何(y=>x.LastName.Contains(y));
}

此处实际上不需要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);
}
将它们与您的示例结合起来:

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);
var predicate = GenerateContains(GenerateConcat<User>(new[] { "FirstName", "LastName" }), "Rog");
var谓词=GenerateContains(generateCat(new[]{“FirstName”,“LastName”}),“Rog”);

尽管t-sql中有CONCAT函数,但我认为EF(特别是L2SQL)功能不支持这种转换。您应该首先使用.ToList()提取所有条目,然后在内存中进行选择。嗯,真的吗?从我的旅行中,我非常确信上面这个流畅的例子会转化为T-SQL,其中FirstName+''+LastName类似于“%Rog%”。为什么不
u=>u.FirstName.Contains(query)| u.LastName.Contains(query)
?因为这样的查询不适合说“Roger Smith”。谢谢。如果我无法实现连接,那么拆分术语并进行基于Any/Or的搜索肯定是一种退路。现在我想了想,我可能会尝试一下,看看它是否给出了足够好的结果。我已经接受了这一点作为前进的方向,尽管我的功能在技术上已经是可行的,尽管它非常冗长,例如
/api/Users?或者\u firstName\u contains=ja&或者\u firstName\u contains=smi&或者\u lastName\u contains=ja&或者\u lastName\u contains=smi
。我知道这一定是可能的,因为Linq已经使用常规的fluent或查询语法来处理它了,不过现在这应该让我明白了。谢谢Ivan-brilliant。我已经更改了已接受的答案,是的,您是正确的。问题实际上只是关于创建适当的lambda表达式。PredicateBuilder只处理各种表达式的链接,以便针对集合执行。
var predicate = GenerateContains(GenerateConcat<User>(new[] { "FirstName", "LastName" }), "Rog");