C# generic search extension method for IQueryable

by John Nye

21 Mar
2013

Now available as a nuget package. Search for 'SearchExtensions' or run the following:

PM> Install-Package NinjaNye.SearchExtensions

Source code can be found here: https://github.com/ninjanye/searchextensions


Following on from my previous post on creating a generic repository method, I decided to take it a step further and create an generic search extension method to perform the same task.

Here is the code:

public static class QueryableExtensions
{
    public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, string searchTerm)
    {
        if (String.IsNullOrEmpty(searchTerm))
        {
            return source;
        }

        // The below represents the following lamda:
        // source.Where(x => x.[property] != null
        //                && x.[property].Contains(searchTerm))

        //Create expression to represent x.[property] != null
        var isNotNullExpression = Expression.NotEqual(stringProperty.Body, 
                                                      Expression.Constant(null));

        //Create expression to represent x.[property].Contains(searchTerm)
        var searchTermExpression = Expression.Constant(searchTerm);
        var checkContainsExpression = Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);

        //Join not null and contains expressions
        var notNullAndContainsExpression = Expression.AndAlso(isNotNullExpression, checkContainsExpression);

        var methodCallExpression = Expression.Call(typeof(Queryable),
                                                   "Where",
                                                   new Type[] { source.ElementType },
                                                   source.Expression,
                                                   Expression.Lambda<Func<T, bool>>(notNullAndContainsExpression, stringProperty.Parameters));

        return source.Provider.CreateQuery<T>(methodCallExpression);
    }
}

Performing the following code against a DBContext (connected to a sql db):

string searchTerm = "test";
var results = context.Clubs.Search(club => club.Name, searchTerm).ToList();

Which produces the following SQL:

SELECT [Extent1].[Id] AS [Id], 
       [Extent1].[Name] AS [Name] 
FROM   [dbo].[Clubs] AS [Extent1]
WHERE  ([Extent1].[Name] IS NOT NULL) 
  AND  ([Extent1].[Name] LIKE N'%test%')

My next goal is to create an extension method that allows the user to pass multiple properties. The results will then match any of the supplied properties. Stay tuned...

Comments 0 * Be the first to comment!

Leave a message...

20 Apr
2024