Range variable name cannot match the name of a member of the 'Object' class. when querying a DataGridView using Linq - vb.net

I am trying to Query a DataGridViewRowsCollection object using LINQ so I don't need a for loop. I want to get the first cells out what is a String and put it in a generic list.
Based on my knowledge and research the query should be the following:
Dim Result As List(Of String) = (From row In gridMappingClasses.Rows.Cast(Of DataGridViewRow)()
Select row.Cells(0).Value.ToString).ToList()
but it fails with the following error: Range variable name cannot match the name of a member of the 'Object' class.
But if I remove the ToString method call and change Result to List(of object) it works fine. Btw I am using Strict on.
Can anyone help?

Try giving it an alias (ToString is already a member of Object, can't have another one):
(From row In gridMappingClasses.Rows.Cast(Of DataGridViewRow)()
Select v = row.Cells(0).Value.ToString).toList
But there is nothing wrong with having a for loop. You should generally use LINQ for simple queries that don't cause any debugging headache. If a LINQ query starts to become problematic, it's time to rewrite it as a loop. In your case it would have resolved the problem.

Related

type var is not defined vb.net

I found an example in C# and from my understanding there is no alternative to 'var' in VB.NET. I am trying to create a datatable that will populate depending on a LINQ command further down in my code that calls this function. I have searched for a solution, but unable to find anything that works. Any assistance on what I should use would be appreciated. Note that I do have both Option Strict and Option Infer on as well.
Private Shared Function ToDataTable(rows As List(Of DataRow)) As DataTable
Dim table As New DataTable()
table.Columns.Add("Title")
table.Columns.Add("Console")
table.Columns.Add("Year")
table.Columns.Add("ESRB")
table.Columns.Add("Score")
table.Columns.Add("Publisher")
table.Columns.Add("Developer")
table.Columns.Add("Genre")
table.Columns.Add("Date")
For Each row As var In rows
table.Rows.Add(row.ItemArray)
Next
Return table
End Function
C# uses 'var' for implicit typing - VB uses Option Infer On combined with omitting the type.
The VB equivalent is:
Option Infer On
...
For Each row In rows
table.Rows.Add(row.ItemArray)
Next row
.NET already has .CopyToDataTable extension for that:
Dim table As DataTable = rows.CopyToDataTable
The VB equivalent is simply Dim, without any strong typing.
Dim sName = "John Henry"
In this example, the compiler infers type String (when Option Infer is set to On).
In your example, you may omit the As var portion. The compiler will infer type DataRow.
Tag your questions well, in this case there is no C# issue. Your problem is your are not writing an actual type on the foreach statement. This will fix it:
For Each row As DataRow In rows
table.Rows.Add(row.ItemArray)
Next

Linq ToList does nothing

I have Option Strict and Option Infer both set "On".
This code works fine:
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
tBoxes.ToList().ForEach(Sub(c) c.DataBindings.Clear())
Why can't I combine them into the one line below (I believe it's related to the fact that the first line above does not set tBoxes to a list but remains an IEnumerable even though I am calling ToList, why is this?)
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
This code results in an error
Expression does not produce a value
This might seem like much ado about nothing but it's not just the reduction to one line, I'd like to understand what's going on here.
VB.NET 2010
The problem is not the ToList call, but List.ForEach Method which is Sub, hence does not have a result and cannot be assigned to a variable.
If you want to use a single line, remove Dim tBoxes =.
Update In fact there is another problem in the above code.
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
is equivalent to
Dim tBoxList = MainForm.Frame2.Controls.OfType(Of TextBox).ToList
Dim tBoxes = From t in tBoxList
so obviously tBoxes is IEnumerable<TextBox>.
Since the from t In .. part is unnecessary in this case, the "oneliner" should be something like this
MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
If you really need a query part, to avoid such confusions, don't forget to enclose it in (..) before calling ToList or other methods like Count, Any etc., like this
(from t In MainForm.Frame2.Controls.OfType(Of TextBox)).ToList.ForEach(Sub(c) c.DataBindings.Clear())
Small description but enough to understand
From t In MainForm.Frame2.Controls.OfType(Of TextBox) 'Filter all object of type text box
.ToList 'Convert IEnemerable(Of TextBox) to a IList type.
.ForEach(Sub(c) c.DataBindings.Clear())' Iterate through list and remove bindg of each text box
Issue is that .ForEach does not return any value so that there is nothing to assign the tBoxes object that you have created. It is just like a void method or Sub in VB.net.

Best way to iterate through Hashtable and conditionally remove entries in VB.NET

In VB.NET, I have a HashTable that I would like to iterate through and conditionally remove entries from. I've written the following code that does the job perfectly, but I'd like to know if there are any creative ways to simplify the code. It just doesn't seem right to have to create a second list to perform this operation.
Here's what I've written:
Dim ModsToRemove As New List(Of String)
For Each ModKey As DictionaryEntry In ModHashTable
If ModKey.Key.ToString.Contains("Criteria") Then
ModsToRemove.Add(ModKey.Key.ToString)
End If
Next
For Each ModKey As String In ModsToRemove
ModHashTable.Remove(ModKey)
Next
Is there another way to perform the same operation that doesn't require the creation of a second list and a second loop? Is it possible to remove entries from something you are iterating through without throwing an error in VB.NET? Is doing so universally a bad idea?
With a little bit of help from Resharper and LINQ, you can simplify your expression in the following ways.
This code block here can be rewritten to use LINQ instead of the embedded IF statement
For Each ModKey As DictionaryEntry In ModHashTable
If ModKey.Key.ToString.Contains("Criteria") Then
ModsToRemove.Add(ModKey.Key.ToString)
End If
Next
Is equivalent to
Dim modsToRemove As List(Of String) = (From modKey As DictionaryEntry In
modHashTable Where modKey.Key.ToString.Contains("Criteria")
Select modKey.Key.ToString).ToList()
Combining this with your actual loop to remove the items from the Hashtable, you should be able to get the equivalent functionality of your example above with the following 3 lines of code:
For Each key As String In (From modkey As DictionaryEntry In modHashTable Where modkey.Key.ToString.Contains("Criteria") Select modkey.Key.ToString).ToList()
modHashTable.Remove(key)
Next

Binding VB.net DataRepeater to dataview items at runtime with aggregation using linq

Hoping someone can help me out with what is probably a dumb question.
I'm trying to use a datarepeater to display data generated via LINQ from a datatable
I've managed to do this fine with a filtered existing datasource using:
Me.Tbl_52TableAdapter.Fill(Me.CBRDataSet.tbl_52)
Dim query =
From dlist In CBRDataSet.tbl_52.AsEnumerable
Where (dlist.Field(Of String)("TL") = "CTS 06")
Select dlist
query.CopyToDataTable().AsDataView()
DataRepeater1.DataSource = query
The problem being that I need to aggregate a field in the dataset into a count.
If I replace the query with:
Dim query =
From CountAgent In CBRDataSet.tbl_52.AsEnumerable
Group CountAgent By PBX = CountAgent.Field(Of String)("TL") Into Count()
Select Count
It then states that:
'CopyToDataTable' is not a member of 'System.Collections.Generic.IEnumerable(Of Integer)'
I've tried to get around it by changing the declaration to:
Dim query As IEnumerable(Of DataRow) =
Which compiles, but I have no idea if it works, and I cant check as I can't find a way to bind a label to the produced count col of the dataview.
If anyone can tell me what I'm doing wrong i'd be most appreciative.
Coming back to my own question, in case it helps anyone else: MSDN had the answer -
You need to overload copyToDataTable as described in
"How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow"
on:
http://msdn.microsoft.com/en-us/library/bb669096.aspx

Linq to Datarow, Select multiple columns as distinct?

basically i'm trying to reproduce the following mssql query as LINQ
SELECT DISTINCT [TABLENAME], [COLUMNNAME] FROM [DATATABLE]
the closest i've got is
Dim query = (From row As DataRow In ds.Tables("DATATABLE").Rows _
Select row("COLUMNNAME") ,row("TABLENAME").Distinct
when i do the above i get the error
Range variable name can be inferred
only from a simple or qualified name
with no arguments.
i was sort of expecting it to return a collection that i could then iterate through and perform actions for each entry.
maybe a datarow collection?
As a complete LINQ newb, i'm not sure what i'm missing.
i've tried variations on
Select new with { row("COLUMNNAME") ,row("TABLENAME")}
and get:
Anonymous type member name can be
inferred only from a simple or
qualified name with no arguments.
to get around this i've tried
Dim query = From r In ds.Tables("DATATABLE").AsEnumerable _
Select New String(1) {r("TABLENAME"), r("COLUMNNAME")} Distinct
however it doesn't seem to be doing the distinct thing properly.
Also, does anyone know of any good books/resources to get fluent?
You start using LINQ on your datatable objects, you run the query against dt.AsEnumberable, which returns an IEnumerable collection of DataRow objects.
Dim query = From row As DataRow In ds.Tables("DATATABLE").AsEnumerable _
Select row("COLUMNNAME") ,row("TABLENAME")
You might want to say row("COLUMNNAME").ToString(), etc. Query will end up being an IEnumerable of an anonymous type with 2 string properties; is that what you're after? You might need to specify the names of the properties; I don't think the compiler will infer them.
Dim query = From row As DataRow In ds.Tables("DATATABLE").AsEnumerable _
Select .ColumnName = row("COLUMNNAME"), .TableName = row("TABLENAME")
This assumes that in your original sql query, for which you used ADO to get this dataset, you made sure your results were distinct.
Common cause of confusion:
One key is that Linq-to-SQL and (the Linq-to-object activity commonly called) LINQ-to-Dataset are two very different things. In both you'll see LINQ being used, so it often causes confusion.
LINQ-to-Dataset
is:
1 getting your datatable the same old way you always have, with data adapters and connections etc., ending up with the traditional datatable object. And then instead of iterating through the rows as you did before, you're:
2 running linq queries against dt.AsEnumerable, which is an IEnumerable of datarow objects.
Linq-to-dataset is choosing to (A) NOT use Linq-to-SQL but instead use traditional ADO.NET, but then (B) once you have your datatable, using LINQ(-to-object) to retrieve/arrange/filter the data in your datatables, rather than how we've been doing it for 6 years. I do this a lot. I love my regular ado sql (with the tools I've developed), but LINQ is great
LINQ-to-SQL
is a different beast, with vastly different things happening under the hood. In LINQ-To-SQL, you:
1 define a schema that matches your db, using the tools in in Visual Studio, which gives you simple entity objects matching your schema.
2 You write linq queries using the db Context, and get these entities returned as results.
Under the hood, at runtime .NET translates these LINQ queries to SQL and sends them to the DB, and then translates the data return to your entity objects that you defined in your schema.
Other resources:
Well, that's quite a truncated summary. To further understand these two very separate things, check out:
LINQ-to-SQL
LINQ-to-Dataset
A fantastic book on LINQ is LINQ in Action, my Fabrice Marguerie, Steve Eichert and Jim Wooley (Manning). Go get it! Just what you're after. Very good. LINQ is not a flash in the pan, and worth getting a book about. In .NET there's way to much to learn, but time spent mastering LINQ is time well spent.
I think i've figured it out.
Thanks for your help.
Maybe there's an easier way though?
What i've done is
Dim comp As StringArrayComparer = New StringArrayComparer
Dim query = (From r In ds.Tables("DATATABLE").AsEnumerable _
Select New String(1) {r("TABLENAME"), r("COLUMNNAME")}).Distinct(comp)
this returns a new string array (2 elements) running a custom comparer
Public Class StringArrayComparer
Implements IEqualityComparer(Of String())
Public Shadows Function Equals(ByVal x() As String, ByVal y() As String) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of String()).Equals
Dim retVal As Boolean = True
For i As Integer = 0 To x.Length - 1
If x(i) = y(i) And retVal Then
retVal = True
Else
retVal = False
End If
Next
Return retVal
End Function
Public Shadows Function GetHashCode(ByVal obj() As String) As Integer Implements System.Collections.Generic.IEqualityComparer(Of String()).GetHashCode
End Function
End Class
Check out the linq to sql samples:
http://msdn.microsoft.com/en-us/vbasic/bb688085.aspx
Pretty useful to learn SQL. And if you want to practice then use LinqPad
HTH
I had the same question and from various bits I'm learning about LINQ and IEnumerables, the following worked for me:
Dim query = (From row As DataRow In ds.Tables("DATATABLE").Rows _
Select row!COLUMNNAME, row!TABLENAME).Distinct
Strangely using the old VB bang (!) syntax got rid of the "Range variable name..." error BUT the key difference is using the .Distinct method on the query result (IEnumerable) object rather than trying to use the Distinct keyword within the query.
This LINQ query then returns an IEnumerable collection of anonymous type with properties matching the selected columns from the DataRow, so the following code is then accessible:
For Each result In query
Msgbox(result.TABLENAME & "." & result.COLUMNNAME)
Next
Hoping this helps somebody else stumbling across this question...