Copy LINQ results to data set/table - vb.net

I’m working on a form that has a few data grid views that are populated from LINQ queries, no problem there it works as it should however that sorting does not work. After doing some reading its because LINQ results do not support sorting.
As I have the LINQ results already is there a way of copying the results into a dataset or datatable then binding the data grid view to that so sorting will work?
Thanks
EDIT:
Thanks to everyone for the answers, sadly I'm off on holiday for 2 weeks so cant try out and upvote the correct one. However when I return it will be on the top of my list

You can use the CopyToDataTable extension method for this.
The standard implementation of this method only works over IEnumerable<T> where T is of type DataRow but there is an example on MSDN of making your own extension method which works on anonymous types.
I've actually not used CopyToDataTable, in the past I've created a similar end result by creating a BindingList which supports sorting and then creating an instance of it with the query as the IList in the constructor, but the CopyToDataTable approach looks a lot cleaner to me.

In the end I used bits from each option and a little tweak to get it to work with nullable fields.
First thing is getting the CopyToDataTable function into VB, this is done here
http://msdn.microsoft.com/en-us/library/bb669096.aspx
When I tried this it would fail if the column was nullable so I searched and found a mod to that code that would work. Here it is in full
Public Function ExtendTable(ByVal table As DataTable, ByVal type As Type) As DataTable
For Each f As FieldInfo In type.GetFields()
If (Not _ordinalMap.ContainsKey(f.Name)) Then
Dim dc As DataColumn
dc = If(table.Columns.Contains(f.Name), table.Columns(f.Name), table.Columns.Add(f.Name, f.FieldType))
_ordinalMap.Add(f.Name, dc.Ordinal)
End If
Next f
For Each p As PropertyInfo In type.GetProperties()
If Not _ordinalMap.ContainsKey(p.Name) Then
Dim colType As Type = p.PropertyType
If (colType.IsGenericType) AndAlso (colType.GetGenericTypeDefinition() Is GetType(Nullable(Of ))) Then
colType = colType.GetGenericArguments()(0)
End If
Dim dc As DataColumn = IIf(table.Columns.Contains(p.Name), table.Columns(p.Name), table.Columns.Add(p.Name, colType))
_ordinalMap.Add(p.Name, dc.Ordinal)
End If
Next
Return table
End Function
Upvotes all round as they all would have worked I just took this option as it is neater

In C# I do it thus:
DataTable dt = new DataTable();
dt.Columns.Add("a", Type.GetType("System.String"));
dt.Columns.Add("b", Type.GetType("System.String"));
dt.Columns.Add("c", Type.GetType("System.String"));
dt.Columns.Add("d", Type.GetType("System.String"));
foreach (var row in [linqQueryName] )
{
DataRow destRow = dt.NewRow();
destRow["a"] = row.linqCol1;
destRow["b"] = row.linqCol2;
destRow["c"] = row.linqCol3;
destRow["d"] = row.linqCol4;
dt.Rows.Add(destRow);
}

See the code:
Here testData is the data from LINQ query on a list of Class having ID and Name as properties
Dim dataTable As New DataTable()
dataTable.Columns.Add("ID", GetType(Integer))
dataTable.Columns.Add("Name", GetType(String))
For Each item As var In testData
Dim dataRow As DataRow = dataTable.NewRow()
dataRow("ID") = item.ID
dataRow("Name") = item.Name
dataTable.Rows.Add(dataRow)
Next

Related

Reflection Optimization

I've recently implemented reflection to replace the more tedious aspects of our data retrieval from a SQL database. The old code would look something like this:
_dr = _cmd.ExecuteReader (_dr is the SQLDataReader)
While _dr.Read (_row is a class object with public properties)
_row.Property1 = Convert.ToInt16(_dr("Prop1"))
_row.Property2 = Convert.ToInt16(_dr("Prop2"))
_row.Property3 = Convert.ToInt16(_dr("Prop3"))
If IsDBNull(_dr("Prop4")) = False Then _row.Prop4 = _dr("Prop4")
...
Since my code base has a lot of functionality like this, reflection seemed like a good bet to simplify it and make future coding easier. How to assign datareader data into generic List ( of T ) has a great answer that was practically like magic for my needs and easily translated into VB. For easy reference:
Public Shared Function GenericGet(Of T As {Class, New})(ByVal reader As SqlDataReader, ByVal typeString As String)
'Dim results As New List(Of T)()
Dim results As Object
If typeString = "List" Then
results = New List(Of T)()
End If
Dim type As Type = GetType(T)
Try
If reader.Read() Then
' at least one row: resolve the properties
Dim props As PropertyInfo() = New PropertyInfo(reader.FieldCount - 1) {}
For i As Integer = 0 To props.Length - 1
Dim prop = type.GetProperty(reader.GetName(i), BindingFlags.Instance Or BindingFlags.[Public])
If prop IsNot Nothing AndAlso prop.CanWrite Then
props(i) = prop
End If
Next
Do
Dim obj = New T()
For i As Integer = 0 To props.Length - 1
Dim prop = props(i)
If prop Is Nothing Then
Continue For
End If
' not mapped
Dim val As Object = If(reader.IsDBNull(i), Nothing, reader(i))
If val IsNot Nothing Then SetValue(obj, prop, val)
Next
If typeString = "List" Then
results.Add(obj)
Else
results = obj
End If
Loop While reader.Read()
End If
Catch ex As Exception
Helpers.LogMessage("Error: " + ex.Message + ". Stacktrace: " + ex.StackTrace)
End Try
Return results
End Function
The only caveat with this is that it is somewhat slower.
My question is how to optimize. Sample code I find online is all in C# and does not convert neatly into VB. Scenario 4 here seems like exactly what I want, but converting it to VB gives all kinds of errors (Using CodeFusion or converter.Telerik.com).
Has anyone done this in VB before? Or can anyone translate what's in that last link?
Any help's appreciated.
Couple ideas for you.
Don't use the DataReader when reading ALL records at once, it is slower than using a DataAdapter.
When you use the DataAdapter to fill a DataSet, you can iterate through the rows and columns which does NOT use reflection and will be much faster.
I have a program I created (and many other programmers do this too) that generate the code from a database for me. Each table and row is a class that is specifically named an generated in such a way that I can use intellisense and prevents many run-time errors by making them compile-time errors when data changes. This is very much like the EntityFramework but lighter because it fits MY specific needs.

Visual Basic beginner bugs

I am having trouble with a piece of sample code I am using in a Visual Basic project.
This is the sample:
Dim dataRow As DataRow
dataRow = dataSet.Tables(0).NewRow()
I am getting a NullReferenceException on the second line of the sample when I run it.
Any help is greatly appreciated!!!
The most likely explanation is that there is no table at index 0. It may also be that the datSet itself is null.
Not certain as to what the intent of the code is, but from the looks of it you are trying to add a new row to a dataset? If that is the case, you would need declare a new data row and add it to the dataset. Alternatively you should be able to add a row to the dataset and then set datarow to the new row index.
A DataSet is a collection of DataTables. I'm guessing that the DataTable reference has not yet been established and is not pointing to a DataTable object. Check whether the DataTable reference is null (Nothing in VB.NET) and if so, create a new DataTable object and add some columns to it. Then you will be able to add a new row as the DataTable reference will be pointing to a DataTable object that rows can be added to:
If IsNothing(dataset) = True Then
dataset = New DataSet
dataset.Tables.Add("Table1")
End If
If IsNothing(dataSet.Tables(0)) = True Then
dataSet.Tables(0) = New DataTable
dataSet.Tables(0).Columns.Add("FirstName", GetType(String))
dataSet.Tables(0).Columns.Add("Surname", GetType(String))
dataSet.Tables(0).Columns.Add("DateOfBirth", GetType(DateTime))
End If
Dim dataRow As DataRow = dataSet.Tables(0).NewRow
dataRow.Item("FirstName") = "John"
dataRow.Item("Surname") = "Smith"
dataRow.Item("DateOfBirth") = #11/30/1998#
dataSet.Tables(0).Rows.Add(dataRow)

Search Through A DataTable LINQ

I am trying to return a list of data rows where a filed in a datatable matches some criteria. Here is what I have
'Dim returnedList = myDatatable.Where(Function(x) x.Item("TagergetField").ToString = "TheCriteria").ToList()
My Where clause isnt correct and I am not sure how to return a filtered datatable with only the records that match my criteria. I want to return a datatable object not a List
Just try with
datatable.AsEnumerable().Where(Function(x) x("TagergetField").ToString = "TheCriteria").ToList()
this will return a List(Of DataRow)
You could achieve the same result without resorting to Linq
Dim rows = dataTable.Select("TargetField = 'TheCriteria'").ToList()
Note how the last one is more readable and considerably more performant (Just tested)
EDIT
To get a datatable you could use the DataSetExtension namespace method CopyToDataTable
Dim dataTable1 = rows.CopyToDataTable()

datacolumn select by custom object

How can I select from Datatable in VB.net all rows which contains my custom object? - actually its type is INotifyPropertyChanged.
I consider using datatable.select method with filter expression, however no idea how comparision works with select.
Try using LINQ's Where method, something like,
' this will return a list of DataRow object
Dim rows = datatable.AsEnumerable().Where(Function(x) TypeOf x("mycolumn") Is INotifyPropertyChanged).ToList()
OR
' this will return a new DataTable with your selected rows
Dim dt2 = datatable.AsEnumerable().Where(Function(x) TypeOf x("mycolumn") Is INotifyPropertyChanged).CopyToDataTable()

How do I sort a datatable

How do I sort a datatable? I need to return a datatable from a function. I have been struggling with this for hours, and the internet has a few different answers, none of which seem to work for me.
Edit: I want to punch myself. Do a DataView.Sort on your table, then a DataView.ToTable() to put the sorted data into a new dataset...
Example:
Dim view As New DataView(OriginalDataSet) 'Put your original dataset into a dataview
view.Sort = "ColumnName" ' Sort your data view
Dim NewDataSet As DataTable = view.ToTable() ' Put your dataview into a new datatable
End of example
I have a relatively simple example table below, taken from a teaching website. The one twist is that there are duplicate values in the row I am trying to sort on.
Module Module1
Sub Main()
' Get a DataTable instance from helper function.
Dim table As DataTable = GetTable()
End Sub
''' <summary>
''' Helper function that creates new DataTable.
''' </summary>
Function GetTable() As DataTable
' Create new DataTable instance.
Dim table As New DataTable
' Create four typed columns in the DataTable.
table.Columns.Add("Dosage", GetType(Integer))
table.Columns.Add("Drug", GetType(String))
table.Columns.Add("Patient", GetType(String))
table.Columns.Add("Date", GetType(DateTime))
' Add five rows with those columns filled in the DataTable.
table.Rows.Add(25, "Indocin", "David", DateTime.Now)
table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now)
table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now)
table.Rows.Add(21, "Combivent", "Janet", DateTime.Now)
table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now)
table.Rows.Add(21, "Aspirin", "Janet", DateTime.Now)
Return table
End Function
End Module
I have tried selecting into an array, then looping through the array and putting each row into a new datatable, but the select isn't grabbing rows. Example:
drarray = ds.Select("I want to select all here", "MySortColumn")
I have tried various looping strategies, with inner loops, etc and can't seem to figure that out.
I have tried dataTable.DefaultView.Sort = "sortExp" but I can't get that to work.
So what am I missing? I figure with the DefaultView and Select methods I'm just missing something syntactly.
So what's the best way to go, and what am I missing?
You can use something like this:
Return table.Select("","Columns to sort on").CopyToDataTable
Use a DataView to create a view of your data in the DataTable. This allows you to sort, filter, etc. Here's a C# example: Datatable VS dataview
This may help you sortExp can be field on which based the sort should be performed filterExp that should evaluate to true or false. assuming the following fields
Dim filterExp As String = "Patient<> ''"
Dim sortExp As String = "Date "
dt_item.Select(filterExp, sortExp, DataViewRowState.CurrentRows)
The above code shows how to filter and sort the data table dt_item. The filter expression selects Date whose Patient is not NULL. The sort expression causes the results to be sorted by the Date column