RichardW1001
RichardW1001

Reputation: 1985

Linq expressions with Linq to Entities

I'm trying to wrap some repetitive code in an extension method, to clean things up a bit.

The pattern I'm trying to avoid is checking if a string (normally a control's text value) is null/empty, and if it is, comparing it using Contains to a field in my data. Obviously the field isn't hard-coded into my extension, neither is the object type.

What I've got works perfectly in Linq to Objects, but I get the generic run-time error "LINQ to Entities does not recognize the method 'System.String Invoke(GenericQueryHelper.Customer)' method, and this method cannot be translated into a store expression." when using an entity framework model.

Here's what I have:

<System.Runtime.CompilerServices.Extension()>
Public Function CompareAndFilter(Of T)(source As System.Linq.IQueryable(Of T), expressionField As System.Linq.Expressions.Expression(Of System.Func(Of T, String)), compareTo As String)
    If String.IsNullOrEmpty(compareTo) Then
        Return source
    Else
        Dim compiledField As System.Func(Of T, String) = expressionField.Compile()
        Return source.Where(Function(x) compiledField.Invoke(x).Contains(compareTo))
    End If
End Function

And I also tried:

<System.Runtime.CompilerServices.Extension()>
Public Function CompareAndFilter(Of T)(source As System.Linq.IQueryable(Of T), expressionField As System.Func(Of T, String), compareTo As String)
    If String.IsNullOrEmpty(compareTo) Then
        Return source
    Else
        Return source.Where(Function(x) expressionField.Invoke(x).Contains(compareTo))
    End If
End Function

With an identical outcome...

Firstly, is this even possible? I want my usage to look something like this:

Dim results = repository.Customers.CompareAndFilter(Function(c) c.FirstName, searchText)

I do need to get this running against a SQL database really, as it is filtering results, so I don't want to be doing that in memory. Anyone any thoughts?

Upvotes: 1

Views: 1093

Answers (2)

Lukazoid
Lukazoid

Reputation: 19426

Linq to entities does not understand how to invoke a delegate, it needs an expression to work out the SQL to generate.

The following might be able to do what you're after:

<System.Runtime.CompilerServices.Extension()>
Public Function CompareAndFilter(Of T)(source As System.Linq.IQueryable(Of T), expressionField As System.Linq.Expressions.Expression(Of System.Func(Of T, String)), compareTo As String)
    If String.IsNullOrEmpty(compareTo) Then
        Return source
    Else
        Return source.GroupBy(expressionField)
            .Where(Function(g) g.Key.Contains(compareTo))
            .SelectMany(Function(g) g)
    End If
End Function

GroupBy is being used to select the field specified by expressionField, then the key is checked, and the items in each group returned.

Upvotes: 2

StriplingWarrior
StriplingWarrior

Reputation: 156728

Yes, it's possible, but it involves expression manipulation. You can either parse the expression you're given and build the new criterion expression yourself, or have LINQKit perform a tree traversal for you.

Upvotes: 3

Related Questions