Alex Rivera | Logout

How to stay DRY whilst using LINQ to Entities and helper methods?

Asked 2011-08-15T14:44:09.057
9

Lets say that I have a particular way of deciding whether some strings "match", like this:

public bool stringsMatch(string searchFor, string searchIn)
{
  if (string.IsNullOrEmpty(searchFor))
  {
    return true;
  }

  return searchIn != null &&
    (searchIn.Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) ||
     searchIn.Contains(" " + searchFor));
}

I would like to pull matches out of a database using Linq To Entities and this helper. However, when I try this:

IQueryable<Blah> blahs = query.Where(b => stringsMatch(searchText, b.Name);

I get "LINQ to Entities does not recognize the method..."

If I re-write the code as:

IQueryable<Blah> blahs = query.Where(b =>
      string.IsNullOrEmpty(searchText) ||
      (b.Name != null &&
        (b.Name.Trim().ToLower().StartsWith(searchText.Trim().ToLower()) ||
         b.Name.Contains(" " + searchText)));

Which is logically equivalent, then things work fine. The problem is that the code isn't as readable, and I have to re-write it for each different entity I want to match.

As far as I can tell from questions like this one, what I want to do is impossible at the moment, but I'm hoping that I'm missing something, am I?

Edit
Report

1 Answer

5

If all the 'blahs' (classes) that you will be filtering have the same structure, you can use a simple method like this. The main difference is that it returns an Expression that Linq should be able to parse and it brings in the whole instance and filters on Name instead of bringing in just the string name.

    public static Expression<Func<T, bool>> BuildStringMatch<T>(string searchFor) where T : IHasName
    {
        return b =>
              string.IsNullOrEmpty(searchFor) ||
              (b.Name != null &&
                (b.Name.Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) ||
                 b.Name.Contains(" " + searchFor)));
    }

You can use that method like this:

    IQueryable<Blah> blahs = query.Where(BuildStringMatch<Blah>(searchText));

That assumes all your classes that you'd want to filter on implement some interface such as:

    public interface IHasName
    {
        string Name { get; }
    }

If you want to be filtering on different properties, I don't think that's something you can do with simple code like this. I believe you'll need to build the Expression yourself with reflection (or with the help of a library that uses reflection) - it's still possible but much more difficult.

Edit: It sounds like you need dynamic behavior, so I borrowed some logic from dtb's answer to this question and came up with this:

public static Expression<Func<T, bool>> BuildStringMatch<T>(Expression<Func<T, string>> property, string searchFor)
{
    var searchForExpression = Expression.Constant(searchFor, typeof(string));
    return
        Expression.Lambd
answered 2011-08-15T15:19:06.500

Your Answer