Reputation: 3268
I'm having troubles dealing with lambda expressions. I'm working with MVC under EF, and I trying to create a "MetaModel" of which every Model will inherits and with it a method to List elements with Linq.
Because this is a "MetaModel" I don't know the definition of class.
I'm trying to do this, assume that we have a Model class "Customer"
Public Function List(searchText As String) As List(Of Customer)
With EFContext
Return .Customers.AsQueryable().Where((Function(Customer) Customer.Name.Contains(searchText)).ToList()
End With
End Function
But I want it to be generic, so I passed the property name of an inherit class and I want to be able to search (and sort) by this property.
This is my best approach:
Public MustInherit Class BaseBL
Public Function AutoList(Of T)(conf As ConfigurationBL) As List(Of T)
Dim items As List(Of T)
Dim sortParam = Expression.Parameter(GetType(T), "param")
Dim sortFunc = Expression.Convert(Expression.Property(sortParam, conf.SortField), GetType(Object))
Dim sortExpr = Expression.Lambda(Of Func(Of T, Object))(sortFunc, sortParam).Compile()
Dim searchFunc As Func(Of T, Boolean) = Function(param)
Dim prInfo As PropertyInfo = param.GetType.GetProperty(conf.SearchField)
Dim str As String = prInfo.GetValue(param, Nothing)
Return str.Contains(conf.SearchText)
End Function
Dim searchExpr As Expression(Of Func(Of T, Boolean)) = Function(param) searchFunc(param)
Dim query As IQueryable(Of T) = GetQueryable(Of T)()
items = query.Where(searchExpr).OrderBy(sortExpr).ToList() '.Skip(conf.PageSize * (conf.PageNumber - 1)).Take(conf.PageSize).ToList()
Return items
End Function
Public MustOverride Function GetQueryable(Of T)() As IQueryable(Of T)
End Class
This class is the Parent of generic class T, so the GetQueriable(Of T) method will be implemented on child classes.
ConfigurationBL class contains the sortField, searchField and searchText which will provides the proper configuration.
This almost works. I execute the orderBy with sortExpr it works, if I execute the where with searchExpr it works too.
The problem comes when I try to do all toghether. The it raises an excepction:
"Expression LINQ 'Invoke' node is not supported in LINQ to Entities"
I'm pretty sure that this is because I use
Dim searchExpr As Expression(Of Func(Of T, Boolean)) = Function(param) searchFunc(param)
Instead of a proper Expression Tree, but I dont know how to translate searchFunc to an expression tree.
I really stuck with this, I would really appreciate any help and ideas, Thank you :)
Upvotes: 2
Views: 138
Reputation: 3268
Finally I was able to fix it myself, I replaced the searchExpr with this
Dim searchExpr = getSearchExpression(Of T)(conf.SearchField, conf.SearchText).Compile()
where...
Private Function getSearchExpression(Of T)(propName As String, search As String) As Expression(Of Func(Of T, Boolean))
Dim element As ParameterExpression = Expression.Parameter(GetType(T), "element")
Dim propertyValue As ParameterExpression = Expression.Variable(GetType(String), "propertyValue")
Dim searchText As ParameterExpression = Expression.Variable(GetType(String), "searchText")
Dim asgPropertyValue As Expression = Expression.Assign(propertyValue,
Expression.Convert(Expression.Property(element, propName), GetType(String))
)
Dim asgSearchText As Expression = Expression.Assign(searchText,
Expression.Constant(search)
)
Dim callContains As Expression = Expression.Call(propertyValue, GetType(String).GetMethod("Contains"), searchText)
Dim block As Expression = Expression.Block(GetType(Boolean),
New List(Of ParameterExpression)({propertyValue, searchText}),
asgPropertyValue,
asgSearchText,
callContains
)
Return Expression.Lambda(Of Func(Of T, Boolean))(block, element)
End Function
I hope this may help somebody, Greetings :)
Upvotes: 1