Question

I recently posted a question on how to write customized expression tree (though I didn't realize that was what I was asking at the time). Now I am trying to use the answer provided by Scott Chamberlain to create another generic method.

I am using Entity Framework and have several Entities that can be filtered by something like a "Name" property. It isn't always called Name, but that is basically what it is.

The actual filter method is more complex than a simple contains or starts with. It is more like a Starts With, then append any records with a name containing the search text to the end of the list.

Since there are so many Entities that will need to perform this filter, I would like to be able to create an Extension Method that applies that filter to the pre-compiled query.

I would like to be able to call

    someQuery.ContainsName(x => x.Name, filter.Name);

I have an extension method that looks like this

      public static IQueryable<T> ContainsName<T>(this IQueryable<T> query, Expression<Func<T, string>> selector, string matchingName)
    {
        var name = Expression.Constant(matchingName);

        var selectorBody = selector.Body;
        var propertyName = (selectorBody as MemberExpression).Member.Name;
        var propExpression = Expression.Parameter(typeof(string), propertyName);

        var selectorParameters = selector.Parameters;

        Expression check = Expression.Call(typeof(GenericNameFilter).GetMethod("IsMatch"), propExpression, name);

        var lambada = Expression.Lambda<Func<T, bool>>(check, selectorParameters);

        return query.Where(lambada) 
    }

IsMatch just takes in 2 strings and returns a bool. This isn't really the end game for the method, but I wanted to be able to get a simple version of IsMatch working, then I would expand on it.

When I call ToList on the result, I get an exception.

"LINQ to Entities does not recognize the method 'Boolean IsMatch(System.String, System.String)' method, and this method cannot be translated into a store expression."

From the searches I have done I believe what I am trying to do is possible, I am just doing it incorrectly. Is it in the way I am setting up my expression tree? Is it the IsMatch method? Is everything about this wrong?

Was it helpful?

Solution

The gist of the problem is that you can't write query operations that result in code being run, because the database obviously cannot run your C# code on its data.

Your extension method adds a lambda call to the query pipeline; you intend it to be resolved to some overload of the IsMatch method. However, that means nothing to the database so the EF query provider tells you that it cannot translate what you wrote to SQL. Unfortunately there is no way around that.

The same code would run just fine on LINQ to objects because in that scenario the compiler can of course arrange for your method to be called.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top