Reputation: 1985
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
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
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