Brett
Brett

Reputation: 437

Reusable LINQ query except for where clause

I've got a collection of movies which have various properties (title, release year, rating, etc) that I need to search for using a LINQ query as follows:

public BindingList<Movie> SearchByTitle(string title)
{
    var matches = from movies in movieCollection
                  where movies.Title == title
                  select movies;
    // do some other stuff with the matches
}

But I don't want a separate method to search for each property since the only thing that changes between searches is the where section. For example where movies.Rating == rating or where movies.ReleaseYear == releaseYear. How do I make the search method reusable for all different kinds of searches by passing in some sort of Expression or Func as the where section?

Upvotes: 1

Views: 1233

Answers (3)

Jon Skeet
Jon Skeet

Reputation: 1502406

How do I make the search method reusable for all different kinds of searches by passing in some sort of Expression or Func as the where section?

Your query really isn't anything other than the where clause. But you can easily make the where part configurable... just not using query expressions.

public BindingList<Movie> SearchByTitle(Expression<Func<Movie, bool>> predicate)
{
    var matches = movies.Where(predicate);

    // Do common stuff with the matches.
}

EDIT: I was assuming that movies was an IQueryable<T>, given that you were talking about Expression. If it's just an IEnumerable<T>, you want:

public BindingList<Movie> SearchByTitle(Func<Movie, bool> predicate)
{
    var matches = movies.Where(predicate);

    // Do common stuff with the matches.
}

Upvotes: 5

nerdybeardo
nerdybeardo

Reputation: 4685

You can use an extension method (define this in a static class)

    public static IQueryable<T> AddSearchParameter<T>(this IQueryable<T> query, bool condition, System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
        if (condition)
        {
            query = query.Where(predicate);
        }

        return query;
    }

So for example:

public BindingList<Movie> Search(string title, int? year, int? rating)
{
    var matches = movieCollection.AddSearchParameter(!string.IsNullorEmpty(title), m=>m.Title == title);
    matches = matches.AddSearchParameter(year.HasValue, m=>m.Year == year.Value);
    matches = matches.AddSearchParameter(rating.HasValue, m=>m.rating >= rating.Value);

    // do some other stuff with the matches
}

If you're using this against a database it will not actually execute the query until you enumerate so this will not make multiple calls to your database.

Upvotes: 1

Cacho Santa
Cacho Santa

Reputation: 6914

You could use a CompiledQuery.

Check this very interesting answer on SO.

Hope it helps.

Upvotes: 0

Related Questions