我正在尝试对IQueryable
对象进行关键字搜索,但有没有办法在没有先将其转换为列表的情况下执行此操作?
我的代码:
var customers = Customer.IQueryableAll(); // Method returns IQueryable containing all customers.
var customerHolder = new List<Customer>();
foreach(var k in keywords) //keywords = List<string>
{
customerHolder.AddRange(customers.Where(x=>x.FirstName.Contains(k) || x.LastName.Contains(k) || x.CompanyName.Contains(k)).ToList())
}
return customerHolder.Distinct();
如果我想一次显示所有结果,这是有效的,但问题出现在我想要进行分页的地方。在分页之前,该函数仍然会从数据库中获取所有条目,因此它对大型表的效率非常低。 (即customerHolder.Skip(5).Take(5);
)
有没有办法将foreach搜索部分集成到查询本身?
即。
customers.Where( x => x.Name.Contains(anythingInKeyWords));
编辑:为了进一步澄清,我确实想要维护上面的OR ,因此使用多个where子句进行过滤和重新过滤将不起作用。 IE浏览器。 Bill Job / Bill Gates&gt;搜索比尔盖茨应该返回两个条目,因为比尔匹配。
答案 0 :(得分:5)
您需要构建一个查询,为每个实体的每个关键字计算过滤表达式的结果,如果不使用动态LINQ,这个查询就不太实用了。这是一个扩展方法,可以为您做到这一点:
public static class ExtensionMethods
{
public static IQueryable<TEntity> TestPerKey<TEntity, TKey>(
this IQueryable<TEntity> query,
IEnumerable<TKey> keys,
Expression<Func<TEntity, TKey, bool>> testExpression )
{
// create expression parameter
var arg = Expression.Parameter( typeof( TEntity ), "entity" );
// expression body var
Expression expBody = null;
// for each key, invoke testExpression, logically OR results
foreach( var key in keys )
{
// constant expression for key
var keyExp = Expression.Constant( key );
// testExpression.Invoke expression
var invokeExp = Expression.Invoke( testExpression, arg, keyExp );
if( null == expBody )
{
// first expression
expBody = invokeExp;
}
else
{
// logically OR previous expression with new expression
expBody = Expression.OrElse( expBody, invokeExp );
}
}
// execute Where method w/ created filter expression
return query.Where( ( Expression<Func<TEntity, bool>> )Expression.Lambda( expBody, arg ) );
}
}
用法:
class TestEntity
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
}
static void Main()
{
var testCollection = new TestEntity[]{
new TestEntity(){
Id = 0,
FirstName = "abc",
LastName = "def",
CompanyName = "ghi"
},
new TestEntity(){
Id = 1,
FirstName = "def",
LastName = "ghi",
CompanyName = "jkl"
},
new TestEntity(){
Id = 2,
FirstName = "ghi",
LastName = "jkl",
CompanyName = "mno"
},
new TestEntity(){
Id = 3,
FirstName = "bcd",
LastName = "efg",
CompanyName = "hij"
},
};
var keywords = new[]{
"abc",
"jkl"
};
var query = testCollection.AsQueryable()
.TestPerKey(
keywords,
( t, k ) =>
t.FirstName.Contains( k ) ||
t.LastName.Contains( k ) ||
t.CompanyName.Contains( k ) );
foreach( var result in query )
{
Console.WriteLine( result.Id );
}
}
更新 - 尝试以下扩展方法。它更具体,但应与EF合作:
public static IQueryable<TestEntity> TestPerKey(
this IQueryable<TestEntity> query,
IEnumerable<string> keys )
{
MethodInfo containsMethodInfo = typeof( string ).GetMethod( "Contains" );
// create expression parameter
var arg = Expression.Parameter( typeof( TestEntity ), "entity" );
// expression body var
Expression expBody = null;
// for each key, invoke testExpression, logically OR results
foreach( var key in keys )
{
var expression = Expression.OrElse(
Expression.OrElse(
Expression.Call( Expression.Property( arg, "FirstName" ), containsMethodInfo, Expression.Constant( key ) ),
Expression.Call( Expression.Property( arg, "LastName" ), containsMethodInfo, Expression.Constant( key ) ) )
, Expression.Call( Expression.Property( arg, "CompanyName" ), containsMethodInfo, Expression.Constant( key ) ) );
if( null == expBody )
{
// first expression
expBody = expression;
}
else
{
// logically OR previous expression with new expression
expBody = Expression.OrElse( expBody, expression );
}
}
// execute Where method w/ created filter expression
return query.Where( ( Expression<Func<TestEntity, bool>> )Expression.Lambda( expBody, arg ) );
}
答案 1 :(得分:0)
您需要构建完整的IQueryable查询,并在最后执行它。 试试这样:
keywords.All(k=>{customers = customers.Where(x=>x.FirstName.Contains(k) || x.LastName.Contains(k) || x.CompanyName.Contains(k)); return true;});
var result = customers.ToList();
答案 2 :(得分:-5)
不,您必须使用ToList来获得所需的结果,因为LINQ完全是集合基础对象术语。