You need to build a query that ORs the result of your filter expression for each keyword per entity, which isn't very practical without using dynamic LINQ. Here's an extension method that will do just that for you:
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 ) );
}
}
Usage:
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 );
}
}
Update - try the following extension method. It is more specific but should work with 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 ) );
}