VB Running grouped LINQ into DataTable - vb.net

I'm trying to run a LINQ to datatable, that normally works for me. This time I'm trying something new to me, Grouping and taking the first row from each group. questionGroups is of type IEnumerable(Of Object). When I open the query in the debugger I see a collection of DataRows, each with an ItemArray of the values I expect in the columns. How do I run this query to a DataTable, or select the two columns I want and run those into a Dictionary?
Public member 'ToTable' on type 'WhereSelectEnumerableIterator(Of
VB$AnonymousType_0(Of Object,IEnumerable(Of Object)),Object)' not
found.
Dim answerGroup As String = "QuestionSortKey"
Dim answerNo As String = "AnswerNo"
Dim surveyDefinitionNo As String = "Pk_SurveyDefinitionNo"
Dim query = _
From rows In surveyAnswerKeys.Rows _
Where rows(answerNo) IsNot Nothing _
Order By Guid.NewGuid() _
Group By questionSortKey = rows(answerGroup) _
Into questionGroups = Group _
Select questionGroups.First()
Dim randomAnswerNos As DataTable = query.ToTable
Edit: This question is an offshoot of this one: VB LINQ - Take one random row from each group

Related

Min Max in LInq with VB.NET

I am needing help to using LINQ expressions under VB.NET.
I have a Class with 3 fields, I want to find the MIN and MAX of the COMBINED 3 fields (think SQL UNION). I have been able to cobble-up a LINQ expression that puts all 3 (decimal) fields into 1 (decimal) field... and it "should" be as easy as specifying the .Min or .Max property at the end of the expression... however, it keeps throwing an error for me... so, looking for some help.
Here is the LINQ expressions in VB.NET that returns a LIST of the UNION'd columns/fields...
Dim result As List(Of RValue) = MeterRoughnessMaster_R1.[Select](Function(x) New RValue() With { _
.RValue = x.RValueBOTH _
}).Union(MeterRoughnessMaster_R1.[Select](Function(x) New RValue() With { _
.RValue = x.RValueUP _
}).Union(MeterRoughnessMaster_R1.[Select](Function(x) New RValue() With { _
.RValue = x.RValueDN _
}))).ToList
The above needs to be re-worked so that "result" should hold a decimal value or MIN or MAX.
I tried this...
Dim result = MeterRoughnessMaster_R1.[Select](Function(x) New With { _
.RValue = x.RValueBOTH _
}).Union(MeterRoughnessMaster_R1.[Select](Function(x) New With { _
.RValue = x.RValueUP _
}).Union(MeterRoughnessMaster_R1.[Select](Function(x) New With { _
.RValue = x.RValueDN _
}))).Min
But it error'd with "At least one object must implement IComparable." so, I am missing a keyword someplace that would otherwise invoke IComparable (which is what I thought .Min would do)
I want above LINQ expression re-worked to simply return .Min (or .Max) to the variable "result" (decimal). The "RValue" Class can be abandoned if necessary... but the 3 fields from "MeterRoughnessMaster_R1" must remain. I want to do this in LINQ (VB.NET).
There's no need for that elaborate Union. You should be using SelectMany.
Dim allValues = MeterRoughnessMaster_R1.SelectMany(Function(x) {x.RValueBOTH, x.RValueUP, x.RValueDN}).ToArray()
Dim min = allValues.Min()
Dim max = allValues.Max()
Obviously you don't have to use the allValues variable and can get the min or max in one statement but you should use a variable if you want both.
EDIT: I should also note that if you do use a single statement to get just the min or just the max then you can drop the ToArray call too.
Dim min = MeterRoughnessMaster_R1.SelectMany(Function(x) {x.RValueBOTH, x.RValueUP, x.RValueDN}).Min()
Dim max = MeterRoughnessMaster_R1.SelectMany(Function(x) {x.RValueBOTH, x.RValueUP, x.RValueDN}).Max()

JQGrid on ASP.Net MVC with VB.Net

I am trying to make a JQGrid for a simple table.
After following through with a VB translated version from
http://haacked.com/archive/2009/04/14/using-jquery-grid-with-asp.net-mvc.aspx
from
http://www.qa.downappz.com/questions/jqgrid-sorting-in-vb-net-mvc-app.html
I modified it to my own database and came up with this function
Public Function SelectGridData(ByVal sidx As String, ByVal sord As String, ByVal page As Integer, ByVal rows As Integer) As ActionResult
Dim context As New IssueDBEntities
Dim pageIndex As Integer = Convert.ToInt32(page) - 1
Dim pageSize As Integer = rows
Dim totalRecords As Integer = context.Issues.Count()
Dim totalPages As Integer = CInt(Math.Ceiling(CSng(totalRecords) / CSng(pageSize)))
Dim jsonData = New With { _
.total = totalPages, _
.page = page, _
.records = totalRecords, _
.rows = (From p In context.Issues _
Order By (p.ID & " " & sord) _
Select New With {.id = p.ID, .cell = _
{p.ID, p.Image_Path, p.Magazine_Type,p.Magazine_Path}}).ToArray()}
Return Json(jsonData, JsonRequestBehavior.AllowGet)
End Function
The grid does show up without any data, and the system throws an error
The error description says
"Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
Any help is appreciated, if this is due to some basic misunderstanding, please guide me, I am willing to do some hard work.
Thank you
Edit: The code that finally worked as per Oleg's Suggestion
Dim Simple_Object As IQueryable(Of Object)
Dim Second_Simple_Object As IQueryable(Of Object)
Dim My_Array As Array
Dim My_Second_Array As Array
Simple_Object = From p In Context.Issues _
Order By (p.ID & " " & sord) _
Select New With {p.ID, p.Image_Path, p.Magazine_Type, p.Magazine_Path}
My_Array = Simple_Object.ToArray()
Second_Simple_Object = From p In Context.Issues _
Order By (p.ID & " " & sord) _
Select New With {p.ID}
My_Second_Array = Second_Simple_Object.ToArray()
Dim My_Result(0) As My_Record_Type
For i = 0 To My_Array.GetLength(0) - 1
If i > 0 Then
ReDim Preserve My_Result(i)
End If
My_Result(i) = New My_Record_Type
My_Result(i).id = CInt(My_Second_Array(i).ID)
My_Result(i).Cell = {My_Array(i).ID.ToString, My_Array(i).Image_Path.ToString, _
My_Array(i).Magazine_Type.ToString, My_Array(i).Magazine_Path.ToString}
Next
Class My_Record_Type
Public id As Integer
Public Cell As String()
End Class
You try to fill rows property in one step. The problem is that you use Entity Framework which have some restrictions in the conversion of the data types. You can solve the problem if you first make query to get the items which you need without any data conversion and save intermediate results as List of items. After that you can make another LINQ Query where you include explicit conversion of p.ID to string. Moreover your current code don't use any paging of data. So the user will never seen more as the first page if you don't use loadonce: true.
It's difficult for me to write correct code in VB without debugging it. I use C# instead since last years. I recommend you to look at the answer for more information. It contains implementation of the server side filtering. If you don't need it and remove the corresponding code the rest code will be very short and I hope it will be easy to understand.
Here is complete sample of JQgrid with ASP.NET MVC 3 + C# (you can convert it to vb.net)
http://www.dotnetacademy.blogspot.in/2012/04/using-jqgrid-in-aspnet-mvc-3.html

How To Use LINQ To Find Matching Data Against A List Of Strings

I have a specialized string dictionary of (string, string) (_RulesAndTheirDescriptions) that contains the name (key) and description (value) of methods in a given class. I currently do the following query to search for a match on the key or value and then bind that to a grid. Works great!
Dim Results = From v In _RulesAndTheirDescriptions _
Where v.Value.ToString().ToUpper().Contains(Me.txtSearchFor.Text.ToUpper()) _
Or v.Key.ToString().ToUpper().Contains(Me.txtSearchFor.Text.ToUpper()) _
Order By v.Key _
Select New With {.Rule = v.Key, .Description = v.Value.ToString()}
This works great when matching "word" or perhaps even "my word" but I would like to search for "my" and "word" and "also this". Meaning words and phrases seperated by spaces. Much like google and bing. When the user enters a value I only would require that the phrases be quoted. The following RegEx takes care of getting me a list word/phrase the user is looking for. Now I am having a hard time combining the the above query that works with the new enhanced list.
Please excuse the below code. I am just trying to test things and get it working.
Dim b As Match
b = Regex.Match(Me.txtSearchFor.Text, "(?<=(?:^|\s|,)"")[^""]*?(?="")|(?<=\s|^)(?!"")[\w\W]+?(?=\s|$)")
Dim sl As List(Of String) = New List(Of String)
If b.Success Then
sl.Add(b.Value.ToUpper())
Dim sMatch = b.NextMatch()
While sMatch IsNot Nothing AndAlso sMatch.Success
sl.Add(sMatch.Value.ToUpper())
sMatch = sMatch.NextMatch()
End While
End If
Per another post on this site I tried to do the following but that is not returing any results. I suspect because the sl.ToString() returns the type and not the value?
Dim Results = From v In _RulesAndTheirDescriptions _
Where v.Value.ToString().ToUpper().Contains(sl.ToString()) _
Order By v.Key _
Select New With {.Rule = v.Key, .Description = v.Value.ToString()}
If I am going about this all wrong, please enlighten me. Seems like it should be easy.
Thanks in advance,
Kevin
Hello You have to have a cross join to solve this problem
Dim Results = From v In _RulesAndTheirDescriptions _
join y in sl on 1 equals 1
Where v.Value.ToString().ToUpper().Contains(y) _
Order By v.Key _
Select New With {.Rule = v.Key, .Description = v.Value.ToString()}
This can produce duplicates when more than one token can match a rule.So you can do a distinct if you want unique values
Like Results.Distinct()

VB.Net I'm trying to write an extension for a generic linq search, however I'm not sure how to return more than one result 0.o

I'm a bit new to vb.net and used to working in perl, this is what I'd like to do.
I wanted something similar to DBIX::Class::Resultset's search (from cpan) in my vb.net project, so that I can give my function a hash containing keys and values to search on a table.
Currently it returns a single matching result of type T where I want it to return all results as a data.linq.table(of T)
How should I alter my expression.lambda so that I can say table.Select(Predicate) to get a set of results? After that I think it should be as simple as saying results.intersect(result) instead of Return test.
Any help will be very much appreciated.
Thanks in advance
-Paul
<System.Runtime.CompilerServices.Extension()> _
Public Function Search(Of T As Class)(ByVal context As DataContext, _
ByVal parameters As Hashtable) As T
Dim table = context.GetTable(Of T)()
Dim results As Data.Linq.Table(Of T)
For Each Parameter As DictionaryEntry In parameters
Dim column As Object = Parameter.Key
Dim value As String = Parameter.Value
Dim param = Expression.Parameter(GetType(T), column)
Dim Predicate = Expression.Lambda(Of Func(Of T, Boolean)) _
(Expression.[Call](Expression.Convert(Expression.Property(param, column), _
GetType(String)), GetType(String).GetMethod("Contains"), _
Expression.Constant(value)), New ParameterExpression() {param})
Dim test = table.First(Predicate)
Return test
' result.intersect(result)
Next
'Return results
End Function
This works assuming you want an "AND" conjunction between predicates
For instance:
Dim h = New System.Collections.Hashtable
h.Add("FieldA", "01 5149")
h.Add("FieldB", "WESTERN")
Dim t = (New DBDataContext).Search(Of DBrecord)(h)
Debug.Print(t.Count.ToString)
Would return those records where fieldA matched AND fieldb matched.
If you wanted OR, DiceGuy's right, use UNION.
Here's the search...
Note, I used STARTSWITH instead of contains because it's alot faster for large sets
You can always change it back.
<System.Runtime.CompilerServices.Extension()> _
Public Function Search(Of T As Class)(ByVal context As DataContext, _
ByVal parameters As Hashtable) As IQueryable(Of T)
Dim table = context.GetTable(Of T)()
Dim results As IQueryable(Of T) = Nothing
For Each Parameter As DictionaryEntry In parameters
Dim column = DirectCast(Parameter.Key, String)
Dim value As String = DirectCast(Parameter.Value, String)
Dim param = Expression.Parameter(GetType(T), column)
Dim Predicate = Expression.Lambda(Of Func(Of T, Boolean)) _
(Expression.[Call](Expression.Convert(Expression.Property(param, column), _
GetType(String)), GetType(String).GetMethod("StartsWith", New Type() {GetType(String)}), _
Expression.Constant(value)), New ParameterExpression() {param})
Dim r = table.Where(Predicate)
If results Is Nothing Then
results = r
Else
results = results.Intersect(r)
End If
Next
Return results
End Function
Well, for starters let's change the return type to Data.Linq.Table(Of T).
Then instead of table.First(Predicate), try table.Where(Predicate)
Finally 'Intersect' will only give you results that contain all your parameters. If that's what you want, then fantastic! If not, then try 'Union' instead.
Let me know where that gets you and we can work from there.

Linq to object: ToList can not convert to generic list

Dim tenItem = From t In _InvalidFeeList _
From i In ItemCount _
Where t.FeeCode <> i.FeeCode _
Order By t.Description _
Ascending Take (10)
Dim feeList As List(Of AccessorialFee) = tenItem.ToList()
I am getting "Can not convert to System.generic.list" error. I look all over for this error and it seem like my code should work for example here
I don't get it. Can someone tell me where i got it wrong?
edit: I think i should explain what I'm trying to do here. I want to compare two object list and select the object from the InvalidFeeList if the FeeCode is not equal to the object in ItemCount FeeCode and take the first 10 objects from the InvalidFeeList.
The problem is that you've got two "From" clauses, so the result isn't just the type of the original collection. It's easy to fix though - just add a projection at the end:
Dim tenItem = From t In _InvalidFeeList _
From i In ItemCount _
Where t.FeeCode <> i.FeeCode _
Order By t.Description _
Ascending Take (10) _
Select t ' This is the new line '
Dim feeList As List(Of AccessorialFee) = tenItem.ToList()
EDIT: Are you trying to find items in _InvalidFeeList whose FeeCode isn't present in any item in ItemCount? If so, I suggest this change:
Dim feeCodes = From i In ItemCount Select i.FeeCode
Dim feeCodeSet = new HashSet(Of FeeCodeType)(feeCodes)
Dim tenItem = From t in _InvalidFeeList
Where Not feeCodeSet.Contains(t.FeeCode)
Order By t.Description _
Ascending Take (10)
Dim feeList As List(Of AccessorialFee) = tenItem.ToList()
As you can see, I didn't know the type of FeeCode, and VB is not exactly my strong point, but I hope you get the general idea. This suggestion is assuming you're using LINQ to Objects - if you're using LINQ to SQL I'm not sure of the best way to do it. Note that we no longer need the Select clause for tenItem as we're only dealing with a single collection now.
_InvalidFeeList is not of type List(Of AccessorialFee)
[Edit] Try this and add a breakpoint and a watch:
Dim tenItem = (From t In _InvalidFeeList _
From i In ItemCount _
Where t.FeeCode <> i.FeeCode _
Order By t.Description _
Ascending Take (10)).ToList().GetType()