PredicateBuilder, VB.net and Where() - vb.net

I am building a predicate in VB.net using the PredicateBuilder class from the LinqKit library.
My datasource is a manually constructed datatable.
All the examples I've found show folks creating the predicate and then passing that predicate as an argument to the Where() method on the datatable.AsEnumerable().
But intellisense is telling me that the Where() method takes a paremeter of type "System.Func", but the type returned by PredicateBuilder is "System.Linq.Expressions.Expression(Of Func(Of T, Boolean))"
What am I missing?
Example:
Dim ds As DataTable = getData()
Dim tmp As IEnumerable(Of DataRow) = New DataTable().AsEnumerable()
' CREATE DYNAMIC LINQ WHERE CLAUSE
Dim predicate As System.Linq.Expressions.Expression(Of Func(Of DataRow, Boolean)) = PredicateBuilder.True(Of DataRow)()
If cbHPMS_ShowRequired.Checked Then
predicate = predicate.And(Function(x As DataRow) x("RECORD_TYPE") = "REQUIRED")
End If
If cbHPMS_ShowOptional.Checked Then
predicate = predicate.And(Function(x As DataRow) x("RECORD_TYPE") = "OPTIONAL")
End If
If cbHPMS_EmptyRecord.Checked Then
predicate = predicate.And(Function(x As DataRow) x("RECORD_STATUS") = "EMPTY")
End If
If cbHPMS_PartialRecord.Checked Then
predicate = predicate.And(Function(x As DataRow) x("RECORD_STATUS") = "PARTIAL")
End If
If cbHPMS_CompletedRecord.Checked Then
predicate = predicate.And(Function(x As DataRow) x("RECORD_STATUS") = "COMPLETE")
End If
If Not String.IsNullOrEmpty(ddHPMS_RoadName.SelectedValue) And Not ddHPMS_RoadName.SelectedValue.Equals("Select") Then
predicate = predicate.And(Function(x As DataRow) x("RoadName") = ddHPMS_RoadName.SelectedValue)
End If
tmp = ds.AsEnumerable().Where(predicate)

I have not used LinqKit but would think it would be like
tmp = ds.AsEnumerable().Where(predicate.Compile())

Related

How build dynamic where clause with string argument

I try to buid a method to add a where clause to a Linq-to-SQL request (return an IQueryable). I try several methods but always use ToString, Indexof... but this result is a sql request take all element and the filter made in linq. I see request in SQL Server profiler.
I want a method to do it with result is a sql request with where include inside
I work in Visual Studio 2017 with SQL Server 2016. I code in vb.net
I see an interesting thing in linq dynamic library. But I can't to adapt to my situation
<Extension()> _
Public Function Where(ByVal source As IQueryable, ByVal predicate As String, ByVal ParamArray values() As Object) As IQueryable
If source Is Nothing Then Throw New ArgumentNullException("source")
If predicate Is Nothing Then Throw New ArgumentNullException("predicate")
Dim lambda As LambdaExpression = DynamicExpression.ParseLambda(source.ElementType, GetType(Boolean), predicate, values)
Return source.Provider.CreateQuery( _
Expression.Call( _
GetType(Queryable), "Where", _
New Type() {source.ElementType}, _
source.Expression, Expression.Quote(lambda)))
End Function
But I don't need all this complex strucutre. It's some years I buid my utilities. But Need to upgrade it. Here my code of my utilities
<Extension()>
Public Function Where(ByVal source As IQueryable, ByVal predicate As String) As IQueryable
Dim param = Expression.Parameter(GetType(String), "x")
Return source.Provider.CreateQuery(
Expression.Call(
GetType(Queryable), "Where",
New Type() {source.ElementType},
source.Expression, Expression.Quote(Expression.Lambda(Expression.Constant(predicate), param))))
End Function
Public Function TFOAppliqueFiltreTri(Of T, MaClassDatas As Class)(Origins As IQueryable(Of T), ByVal MesDonnees As TableFullOption.PagerTabEnCours(Of MaClassDatas)) As IQueryable(of T)
Dim retour As New TableFullOption.LstRetour
'Colonne de filtre
Dim strWh As String = ""
Dim Filtredrecords As IQueryable(Of T)
For Each Sort In MesDonnees.MesOptions
Dim colName = Sort.ColName
If strWh.Length > 0 Then strWh = strWh & " AND "
strWh = strWh & String.Format(colName & " like '%{0}%'", Sort.Search)
Next
If strWh.Length > 0 Then
Filtredrecords = Origins.Where(strWh) '<- Here call Where
Else
Filtredrecords = Origins
End If
Return Filtredrecords
End Function
I get this error:
Aucune méthode générique 'Where' sur le type 'System.Linq.Queryable' n'est compatible avec les arguments de type et les arguments fournis..
Then my problem is to write correctly lambda expression. My predicate argument is : Column1 like '%aaa%'. I want rewrite where method of dynamicLinq to accept string argument :Column1 like '%aaa%' directly
Thanks for your help
Finally after lot of reading in google and few feelings and certainly lot of chance.
Public Function Where(Of TEntity)(source As IQueryable(Of TEntity), searchColumn As List(Of String), searchValue As String) As IQueryable(Of TEntity)
Dim cond As Expression = Nothing
Dim ParamExpr = Expression.Parameter(GetType(TEntity), "x")
Dim conCat2 = GetType(String).GetMethod("Concat", New Type() {GetType(String), GetType(String)})
Dim conCat4 = GetType(String).GetMethod("Concat", New Type() {GetType(String), GetType(String), GetType(String), GetType(String)})
Dim Delim = Expression.Constant("/")
Dim DateName = GetType(SqlFunctions).GetMethod("DateName", New Type() {GetType(String), GetType(Nullable(Of DateTime))})
Dim DatePart = GetType(SqlFunctions).GetMethod("DatePart", New Type() {GetType(String), GetType(Nullable(Of DateTime))})
Dim DblToString = GetType(SqlFunctions).GetMethod("StringConvert", New Type() {GetType(Nullable(Of Double))})
For Each cn In searchColumn
For Each colName In cn.Split("|")
If Not colName.estVide Then
Dim body As Expression = ParamExpr
For Each member In colName.Split(".")
body = Expression.PropertyOrField(body, member)
Next
Dim Tostr As Expression
If body.Type.FullName.Contains("String") Then
Tostr = body
ElseIf body.Type.FullName.Contains("DateTime") Then
Dim day = Expression.Call(Expression.Call(conCat2, Expression.Constant("0"), Expression.Call(DateName, Expression.Constant("day"), body)), "Substring", Nothing, Expression.Constant(0), Expression.Constant(2))
Dim Month = Expression.Call(DatePart, Expression.Constant("MM"), body)
Dim toDouble = Expression.Convert(Month, GetType(Nullable(Of Double)))
Dim mois = Expression.Call(conCat2, Expression.Constant("0"), Expression.Call(Expression.Call(DblToString, toDouble), "Trim", Nothing))
Dim an = Expression.Call(DateName, Expression.Constant("year"), body)
Tostr = Expression.Call(conCat2, Expression.Call(conCat4, day, Delim, mois, Delim), an)
Else
Tostr = Expression.Call(body, "Convert.ToString", Nothing)
'Tostr = Expression.Convert(body, GetType(String))
End If
Dim condPart = Expression.Call(Expression.Call(Tostr, "ToLower", Nothing), "Contains", Nothing, Expression.Call(Expression.Constant(searchValue), "ToLower", Nothing))
If cond Is Nothing Then
cond = condPart
Else
cond = Expression.OrElse(cond, condPart)
End If
End If
Next
Next
Return source.Provider.CreateQuery(Of TEntity)(Expression.Call(GetType(Queryable), "Where", New Type() {GetType(TEntity)}, source.Expression, Expression.Lambda(cond, ParamExpr)))
End Function
Now I've dynamic filter which generated a SQL request with complete clause where

implicit conversion from object to boolean

I keep getting this exception "implicit conversion from object to boolean" for below expression how do i get rid of it?
Dim objRows As IEnumerable(Of DataRow) = (From myRow As DataRow In objDS.Tables(0).AsEnumerable Where myRow.Item(PERSON_ID_LIST) = personid).Cast(of DataRow)
Try
Dim objRows As IEnumerable(Of DataRow) =
From myRow As DataRow In objDS.Tables(0).AsEnumerable()
Where myRow.Field(Of Int32)("PERSON_ID_LIST") = personid
otherwise you have to tell us what PERSON_ID_LIST is.

call ToArray on LINQ select query

I have this query:
Dim test = result.GroupBy(Function(row) groupedindexes.Select(
Function(grpindex) row(grpindex)).ToArray, comp)
I'm building an expression tree. I have already build the part inside the GroupBy function and now I would like to call the ToArray method. Here is the code:
Public Function Grouping(ByVal result As IEnumerable(Of Object()), ByVal groupedindexes As List(Of Integer), ByVal comparer As compare) As Expression
Dim groupbyMethod = GetType(Enumerable).GetMethods(BindingFlags.Public Or BindingFlags.Static).First(Function(m) m.Name = "GroupBy").MakeGenericMethod(GetType(Object()), GetType(System.Collections.Generic.IEqualityComparer(Of Object())))
Dim convertMethod As MethodInfo = Nothing
Dim rowParameter = Expression.Parameter(GetType(Object()), "Row")
Dim indexParameter = Expression.Parameter(GetType(Integer), "grpindex")
Dim methodstring As String
Dim expr As Expression = Nothing
Dim index As Integer
Dim grpindexes As Expression = Expression.Constant(groupedindexes, GetType(System.Collections.Generic.List(Of Integer)))
Dim selectMethod = GetType(Enumerable).GetMethods(BindingFlags.Public Or BindingFlags.Static).First(Function(m) m.Name = "Select").MakeGenericMethod(GetType(Integer), GetType(Object))
Dim toarrayMethod2 = GetType(Enumerable).GetMethods(BindingFlags.Public Or BindingFlags.Static).First(Function(m) m.Name = "ToArray").MakeGenericMethod(GetType(Object))
Dim cmp As Expression = Expression.Constant(comparer, GetType(compare))
Dim fieldselector As Expressions.LambdaExpression
fieldselector = Expression.Lambda(Expression.ArrayAccess(rowParameter, indexParameter), indexParameter)
Dim outerfieldselector As Expressions.LambdaExpression
outerfieldselector = Expression.Lambda(Expression.Call(selectMethod, grpindexes, fieldselector), rowParameter)
expr = Expression.Call(groupbyMethod, Expression.Call(outerfieldselector, toarrayMethod2), cmp)
Return expr
End Function
I get an error message at the line expr = ...: Static method requires null instance, non-static method requires non-null instance.
I already know this error message, but I think, the expression call is correct: I call the ToArray method on outerfieldselector.
Could you help me out? You can copy&paste the code and test it. You can remove the compare class, if necessary.
Thanks.
You have a couple of mistakes:
You're getting the wrong GroupBy method.
Calls to static methods methods like GroupBy and ToArray need a null instance, just like the error says.
Here's the corrected code. Lines changed are the lines that begin with expr = and outerfieldselector = and getting the GroupBy method:
Public Function Grouping(ByVal result As IEnumerable(Of Object()), ByVal groupedindexes As List(Of Integer), ByVal comparer As compare) As Expression
Dim groupbyMethod = GetType(Enumerable).GetMethods(BindingFlags.Public Or BindingFlags.Static).
Where(Function(m) m.Name = "GroupBy").
First(Function(m) m.GetParameters().Count() = 3).
MakeGenericMethod(GetType(Object()), GetType(Object()))
Dim convertMethod As MethodInfo = Nothing
Dim rowParameter = Expression.Parameter(GetType(Object()), "Row")
Dim indexParameter = Expression.Parameter(GetType(Integer), "grpindex")
Dim methodstring As String
Dim expr As Expression = Nothing
Dim index As Integer
Dim grpindexes As Expression = Expression.Constant(groupedindexes, GetType(System.Collections.Generic.List(Of Integer)))
Dim selectMethod = GetType(Enumerable).GetMethods(BindingFlags.Public Or BindingFlags.Static).First(Function(m) m.Name = "Select").MakeGenericMethod(GetType(Integer), GetType(Object))
Dim toarrayMethod2 = GetType(Enumerable).GetMethods(BindingFlags.Public Or BindingFlags.Static).First(Function(m) m.Name = "ToArray").MakeGenericMethod(GetType(Object))
Dim cmp As Expression = Expression.Constant(comparer, GetType(compare))
Dim fieldselector As Expressions.LambdaExpression
fieldselector = Expression.Lambda(Expression.ArrayAccess(rowParameter, indexParameter), indexParameter)
Dim outerfieldselector As Expressions.LambdaExpression
outerfieldselector = Expression.Lambda(Expression.Call(Nothing, toarrayMethod2, Expression.Call(selectMethod, grpindexes, fieldselector)), rowParameter)
Dim test = Enumerable.GroupBy(result, Function(row) groupedindexes.Select(Function(grpindex) row(grpindex)).ToArray(), comparer)
expr = Expression.Call(Nothing, groupbyMethod, Expression.Constant(result), outerfieldselector, Expression.Constant(comparer))
Return expr
End Function

Can you add an If Statement in a LINQ(XML) query?

I feel this function is too long. I can separate it into two functions, but I would prefer to keep them in one for this.
Public Function getTempList(ByVal applicationType As String) As List(Of String)
Dim doc As XDocument = New XDocument
If My.Settings.sortKey = "alpha" Then
Dim XMLquery = From c In doc.<applications>.<app> _
Where c.<appFav>.Value = "true" And c.<appType>.Value = applicationType
Order By CStr(c.<appName>.Value)
Select c.<appName>
Dim tempList As New List(Of String)
For Each result In XMLquery
tempList.Add(result.Value)
Next
Return tempList
ElseIf My.Settings.sortKey = "fav" Then ' ------------------------------------------------------------------------------------
Dim XMLquery = From c In doc.<applications>.<app> _
Where c.<appFav>.Value = "true" And c.<appType>.Value = applicationType
Order By CInt(c.<appClick>.Value) Descending
Select c.<appName>
Dim tempList As New List(Of String)
For Each result In XMLquery
tempList.Add(result.Value)
Next
Return tempList
End If
End Function
Can I somehow put the if statement in the LINQ query itself. The only thing that needs to change here is the order the list is in. Or, is there another way to order the results I am returning ?
I think this would be the simplest:
Public Function getTempList(ByVal applicationType As String) As List(Of String)
Dim doc As XDocument = New XDocument
Dim XMLquery = _
From c In doc.<applications>.<app> _
Where c.<appFav>.Value = "true" And c.<appType>.Value = applicationType _
Select c
If My.Settings.sortKey = "alpha" Then
XMLquery = XMLquery.OrderBy(Function(c) CStr(c.<appName>.Value))
ElseIf My.Settings.sortKey = "fav" Then
XMLquery = XMLquery.OrderByDescending(Function(c) CInt(c.<appClick>.Value))
End If
Return XMLquery.Select(Function(x) x.<appName>.Value).ToList()
End Function
Try this,
Public Function getTempList(ByVal applicationType As String) As List(Of String)
Dim doc As XDocument = New XDocument
Dim XMLquery = From c In doc.<applications>.<app> _
Where c.<appFav>.Value = "true" And c.<appType>.Value = applicationType
Select c
Dim tempList As New List(Of String)
If My.Settings.sortKey = "alpha" Then
XMLquery = XMLquery.OrderBy(Function(c) CStr(c.<appName>.Value))
ElseIf My.Settings.sortKey = "fav" Then
XMLquery = XMLquery.OrderByDescending(Function(c) CInt(c.<appClick>.Value))
End If
For Each result In XMLquery
tempList.Add(result.<appName>.Value)
Next
Return tempList
End Function
You dont need to repeat the whole query.
You can do something like:
Pardon me for VB syntax, I usually code in C#.
Public Function getTempList(ByVal applicationType As String) As List(Of String)
Dim doc As XDocument = New XDocument
Dim tempList As New List(Of String)
Dim XMLquery = From c In doc.<applications>.<app> _
Where c.<appFav>.Value = "true" And c.<appType>.Value = applicationType
Select c.<appName>
If My.Settings.sortKey = "aplha" Then
Order XMLQuery By CStr(c.<appName>.Value) // convert to VB code
If My.Settings.sortKey = "fav" Then
Order XMLQuery By CInt(c.<appClick>.Value) Descending //convert to VB code
For Each result In XMLquery
tempList.Add(result.Value)
Next
Return tempList
End Function

Casting from System.Linq.EnumerableQuery to System.Data.Datatable (using Dynamic LINQ)

I'm trying to get the results from a Dynamic LINQ query into a DataTable. Trying to get the query directly into a DataTable gives me this error:
Unable to cast object of type 'System.Linq.EnumerableQuery`1[DynamicClass1]' to type 'System.Data.DataTable'.
My code is:
Dim query = tbl.AsEnumerable.AsQueryable.Select("new(it[""curr""] as Curry)")
Dim tbl As DataTable = query
Ive tried:
Dim query As IEnumerable(Of DataRow) = tbl.AsEnumerable.AsQueryable.Select("new(it[""curr""] as Curry)").Cast(Of DataRow)()
Dim tbl1 As DataTable = query.CopyToDataTable
but that gives me:
Unable to cast object of type 'DynamicClass1' to type 'System.Data.DataRow'.
Public Shared Function ConvertIEnumerableToDataTableFromProperties(Of T)(ByVal list As IEnumerable(Of T)) As DataTable
Dim table As New DataTable()
Dim fields() As PropertyInfo = GetType(T).GetProperties()
For Each field As PropertyInfo In fields
table.Columns.Add(field.Name, field.PropertyType)
Next
For Each item As T In list
Dim row As DataRow = table.NewRow()
For Each field As PropertyInfo In fields
row(field.Name) = field.GetValue(item)
Next
table.Rows.Add(row)
Next
Return table
End Function