return Datatable from linq query - vb.net

I am trying to return data table from linq query but getting error message. I am using .net framework 4.0 in VS2012.
<Table>
Public Class StaffOfficeAccess
<Column(CanBeNull:=False, IsPrimaryKey:=True, IsDbGenerated:=True)>
Public Property StaffOfficeAccessID As Int32 = 0
<Column>
Public Property StaffID As Int32 = 0
<Column>
Public Property OfficeID As Int32 = 0
<Column(IsVersion:=True, IsDbGenerated:=True)>
Public Property Version As Byte()
End Class
'----------------------------'
Public Function GetByStaffID(ByVal staffID As Int32) As DataTable
Dim query As IEnumerable(Of DataRow) = CType((From oa In db.StaffOfficeAccess.AsEnumerable() _
Join o In db.Office.AsEnumerable() On oa.OfficeID Equals o.OfficeID _
Select oa.OfficeID,
o.OfficeName), Global.System.Collections.Generic.IEnumerable(Of Global.System.Data.DataRow))
Dim table As DataTable = System.Data.DataTableExtensions.CopyToDataTable(query)
Return table
End Function
'-------error----------'
Unable to cast object of type 'd__614[staff.Objects.StaffOfficeAccess,AMIS.Objects.Office,System.Int32,VB$AnonymousType_02[System.Int32,System.String]]' to type 'System.Collections.Generic.IEnumerable`1[System.Data.DataRow]'.
I have tried the example here https://msdn.microsoft.com/en-us/library/bb396189%28v=vs.110%29.aspx but no luck. I don't get CopyToDataTable in VS2012.

Just another approach by using Aggregate extension method MSDN
Dim dt As New DataTable()
dt.Columns.Add("OfficeID", GetType(Integer))
dt.Columns.Add("OfficeName", GetType(String))
Dim query = From oa In db.StaffOfficeAccess.AsEnumerable()
Join o In db.Office.AsEnumerable()
On oa.OfficeID Equals o.OfficeID
Select oa.OfficeID, o.OfficeName
query.Aggregate(Of DataTable)(dt,
Function(dtb, o)
Dim dr As DataRow = dtb.NewRow()
dr.SetField("OfficeID", o.OfficeID)
dr.SetField("OfficeName", o.OfficeName)
dtb.Rows.Add(dr)
Return dtb
End Function)

I have tried this and it works.
Dim dt As New DataTable()
dt.Columns.Add("OfficeID", GetType(Integer))
dt.Columns.Add("OfficeName", GetType(String))
Dim query = From oa In db.StaffOfficeAccess.AsEnumerable() _
Join o In db.Office.AsEnumerable() On oa.OfficeID Equals o.OfficeID _
Select oa.OfficeID,
o.OfficeName
For Each item In query
Dim dr As DataRow = dt.NewRow()
dr("OfficeID") = item.OfficeID
dr("OfficeName") = item.OfficeName
dt.Rows.Add(dr)
Next

Related

VB.NET LINQ to DataSet (SQL 'LEFT OUTER JOIN' alternative)

I would like to join two DataTables and create the third one from the result. The result DataTable should have three columns:
ID
Name
YearOfBirth
My Compile options:
Option explicit: On
Option strict: On
Option compare: Binary
Option infer: Off
Dim dr As DataRow
REM Dt1
Dim Dt1 As New DataTable
Dt1.Columns.Add("ID", GetType(Integer))
Dt1.Columns.Add("Name", GetType(String))
dr = Dt1.NewRow
dr("ID") = 1
dr("Name") = "Peter"
Dt1.Rows.Add(dr)
dr = Dt1.NewRow
dr("ID") = 2
dr("Name") = "Anna"
Dt1.Rows.Add(dr)
dr = Dt1.NewRow
dr("ID") = 3
dr("Name") = "John"
Dt1.Rows.Add(dr)
REM End Dt1
REM Dt2
Dim Dt2 As New DataTable
Dt2.Columns.Add("ID", GetType(Integer))
Dt2.Columns.Add("YearOfBirth", GetType(Integer))
dr = Dt2.NewRow
dr("ID") = 1
dr("YearOfBirth") = 1970
Dt2.Rows.Add(dr)
dr = Dt2.NewRow
dr("ID") = 2
dr("YearOfBirth") = 1980
Dt2.Rows.Add(dr)
REM End Dt2
Dim Dt3 As New DataTable
Dim query As IEnumerable(Of DataRow) = From dr1 In Dt1.AsEnumerable()
Group Join dr2 In Dt2.AsEnumerable()
On dr1.Field(Of Integer)("ID") Equals dr2.Field(Of Integer)("ID")
Into joined = Group
From j In joined.DefaultIfEmpty()
Select New With
{
.ID = dr1.Field(Of Integer)("ID"),
.Name = dr1.Field(Of String)("Name"),
.YearOfBirth = j.Field(Of Integer)("YearOfBirth")
}
Dt3 = query.CopyToDataTable
But I get the error message in editor (VS 2017):
"Error BC36754:
'IEnumerable(Of anonymous type: ID As Integer, Name As String, YearOfBirth As Integer)' cannot be converted to 'IEnumerable(Of DataRow)' because 'anonymous type: ID As Integer, Name As String, YearOfBirth As Integer' is not derived from 'DataRow', as required for the 'Out' generic parameter 'T' in 'Interface IEnumerable(Of Out T)'."
Select New without specifying class name as query result will return anonymous type (such like IEnumerable(Of AnonymousType)), and CopyToDataTable() throwing exception because IEnumerable(Of AnonymousType) cannot be converted directly to IEnumerable(Of DataRow).
Hence, you need to convert anonymous type into DataRow using additional Select method that iterates IEnumerable(Of AnonymousType) contents and returns DataRow with DataTable.NewRow() (using prepared DataTable which includes column names as result set):
' joined DataTable columns
Dim JoinedDT As New DataTable
JoinedDT.Columns.Add("ID", GetType(Integer))
JoinedDT.Columns.Add("Name", GetType(String))
JoinedDT.Columns.Add("YearOfBirth", GetType(Integer))
' other stuff
Dim query As IEnumerable(Of DataRow) = (From dr1 In Dt1.AsEnumerable() _
Group Join dr2 In Dt2.AsEnumerable() _
On dr1.Field(Of Integer)("ID") Equals dr2.Field(Of Integer)("ID") _
Into joined = Group _
From j In joined.DefaultIfEmpty() _
Select New With
{
.ID = dr1.Field(Of Integer)("ID"),
.Name = dr1.Field(Of String)("Name"),
.YearOfBirth = If(j IsNot Nothing, j.Field(Of Integer)("YearOfBirth"), 0)
}).Select(Function(r)
' use `DataTable.NewRow` here
Dim row As DataRow = JoinedDT.NewRow()
row("ID") = r.ID
row("Name") = r.Name
row("YearOfBirth") = r.YearOfBirth
Return row
End Function)
Dt3 = query.CopyToDataTable()
Reference:
Get linq to return IEnumerable<DataRow> result (C# version)

How to convert 2 dimensional arraylist to datatable?

I need a function that returns a datatable, from any arraylist (2 dimensions) as arguments? Thanks for your help
Creating two dimensional Arraylist:
Public Overrides Function Find(Optional ByRef conditions As ArrayList = Nothing) As System.Collections.ArrayList
Dim collection As New ArrayList
Dim cmd ......... ' Select command based on an arraylist of conditions
Dim dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read()
Dim cnt As New contact
cnt .Id() = dr("id")
cnt .Name= dr("name")
'......... other columns are imported
collection.Add(cnt )
End While
dr.Close()
Return collection
End Function
Suitable solution found:
Public Shared Function ArrayListToDataTable(ByVal arraylist1 As ArrayList) As System.Data.DataTable
Dim dt As New System.Data.DataTable()
For i As Integer = 0 To arraylist1.Count - 1
Dim GenericObject As Object = arraylist1.Item(i)
Dim NbrProp As Integer = GenericObject.GetType().GetProperties().Count
For Each item As PropertyInfo In GenericObject.GetType().GetProperties()
Try
Dim column = New DataColumn()
Dim ColName As String = item.Name.ToString
column.ColumnName = ColName
dt.Columns.Add(column)
Catch
End Try
Next
Dim row As DataRow = dt.NewRow()
Dim j As Integer = 0
For Each item As PropertyInfo In GenericObject.GetType().GetProperties()
row(j) = item.GetValue(GenericObject, Nothing)
j += 1
Next
dt.Rows.Add(row)
Next
Return dt
End Function
After 2 years, Let me answer this=>
Function ConvertArrayListToDataTable(ByVal arraylist As ArrayList) As DataTable
Dim dt As DataTable = New DataTable()
If arraylist.Count <= 0 Then
Return dt
End If
Dim propertiesinfo As PropertyInfo() = arraylist(0).GetType().GetProperties()
For Each pf As PropertyInfo In propertiesinfo
Dim dc As DataColumn = New DataColumn(pf.Name)
dc.DataType = pf.PropertyType
dt.Columns.Add(dc)
Next
For Each ar As Object In arraylist
Dim dr As DataRow = dt.NewRow
Dim pf As PropertyInfo() = ar.GetType().GetProperties()
For Each prop As PropertyInfo In pf
dr(prop.Name) = prop.GetValue(ar, Nothing)
Next
dt.Rows.Add(dr)
Next
Return dt
End Function

Only return the latest 'x' files

I have the following code
Public Function ListDirLatest(ByVal Dir As String, ByVal Amount As Integer) As DataTable
Dim dt As DataTable = ListDir(Dir)
If (dt Is Nothing) Then
Return Nothing
Else
Return dt ' This is where i would like to implement the latest x-files logic
End If
End Function
Private Function ListDir(ByVal Dir As String) As DataTable
If Directory.Exists(Dir) Then
Dim dt As DataTable = GetDT()
Dim dirinfo As New DirectoryInfo(Dir)
For Each fsi As FileSystemInfo In dirinfo.GetFileSystemInfos(".txt")
Dim dr As DataRow = dt.NewRow()
dr("FileName") = fsi.Name()
dr("FileDate") = fsi.CreationTime()
Next
Return dt
Else
Return Nothing
End If
End Function
Private Function GetDT() As DataTable
'Create DataTable to hold results
Dim dt As New DataTable("DirList")
Dim st As System.Type = System.Type.GetType("System.String")
dt.Columns.Add("FileName", st)
dt.Columns.Add("FileDate", st)
Return dt
End Function
At the moment the ListDirLatest Function will return all the files the in the directory.
How do I change the code so that it only returns the latest 'x' files, as specified by the Amount argument.
To Clarify I want to return the LATEST 'x' files in the directory.
You can solve your problem with a little of Linq and a the reference to System.Data.DataSetExtensions
( http://msdn.microsoft.com/en-us/library/system.data.datatableextensions(v=vs.100).aspx )
Public Function ListDirLatest(ByVal Dir As String, ByVal Amount As Integer) As DataTable
Dim dt As DataTable = ListDir(Dir)
If (dt Is Nothing) Then
Return Nothing
Else
Dim r = from myRow in dt.AsEnumerable()
Order By("FileDate DESC")
Take(Amount)
dt = r.CopyToDataTable()
return dt
End If
End Function
Also, the ListDir function has a couple of errors
Add the row information to the DataTable returned
Use a correct pattern for GetFileSystemInfos
Function ListDir(ByVal Dir As String) As DataTable
If Directory.Exists(Dir) Then
Dim dt As DataTable = GetDT()
Dim dirinfo As New DirectoryInfo(Dir)
For Each fsi As FileSystemInfo In dirinfo.GetFileSystemInfos("*.txt")
Dim dr As DataRow = dt.NewRow()
dr("FileName") = fsi.Name()
dr("FileDate") = fsi.CreationTime()
dt.Rows.Add(dr)
Next
Return dt
Else
Return Nothing
End If
End Function
If you mean by Amount the number of files to be returned, then here is what you need to do:
First Change the header of ListDir function to accept a parameter to allow counting the number of files to be returned, and pass that parameter from the first function,
Public Function ListDirLatest(ByVal Dir As String, ByVal Amount As Integer) As DataTable
Dim dt As DataTable = ListDir(Dir, Amount)
If (dt Is Nothing) Then
Return Nothing
Else
Return dt ' This is where i would like to implement the latest x-files logic
End If
End Function
Private Function ListDir(ByVal Dir As String, ByVal Amount As Integer) As DataTable
If Directory.Exists(Dir) Then
Dim dt As DataTable = GetDT()
Dim dirinfo As New DirectoryInfo(Dir)
Dim cnt as Integer = 0
For Each fsi As FileSystemInfo In dirinfo.GetFileSystemInfos(".txt")
Dim dr As DataRow = dt.NewRow()
dr("FileName") = fsi.Name()
dr("FileDate") = fsi.CreationTime()
cnt += 1
if cnt >= Amount Then Exit For
Next
Return dt
Else
Return Nothing
End If
End Function

Datatable.Select with Like in VB.net

I have a datatable where i am trying to do
datatable.Select(Name Like '#%#') but getting error that invalid pattern(expecting result of a table with name col having #Mike#,#Brow#..). Using escape sequense dint for all items dint work fine too. Many suggest to use Linq - but am new to it. How can i do this filter with Linq from this datatable.
This is a sample of what i was trying to do..
Dim dtSamp As Data.DataTable
dtSamp = New Data.DataTable
dtSamp.Columns.Add("Name")
dtSamp.Columns.Add("Marks")
Dim dr As DataRow
dr = dtSamp.NewRow()
dr.Item(0) = "AAA"
dr.Item(1) = "50"
dtSamp.Rows.Add(dr)
dr = dtSamp.NewRow()
dr.Item(0) = "#bbb#"
dr.Item(1) = "60"
dtSamp.Rows.Add(dr)
dr = dtSamp.NewRow()
dr.Item(0) = "ccc"
dr.Item(1) = "44"
dtSamp.Rows.Add(dr)
Dim drResult As DataRow()
drResult = dtSamp.Select("Name Like '#%#'")
Dim dtOutPutTable As Data.DataTable
dtOutPutTable = drResult.CopyToDataTable()
In the dtOutPutTable i was expecting 1 row ie, #bbb# in it.. but the Select function fails.
Generally LINQ queries works on data sources which implement the IEnumerable<T>/ IQueryable<T> Interface. But DataTable does not implement any of these. So we can not directly apply LINQ queries on a DataTable.
But DataTable class has an extension method called AsEnumerable which returns an IEnumerable collection of DataRow. So we can apply the AsEnumerable function on a DataTable and then play with some LINQ on the resulting collection.
var items=(from p in myDataTable.AsEnumerable()
select new { ID= p.Field<int>("ID").
Name=p.Field<string>("Name")
}).ToList();
var filtered=items.Where(x => x.Name.Contains("Mike"));
EDIT : Here is the VB.NET Version ( Disclaimer: I am not a VB.NET guy. but i could build this code without any error)
Dim items = (From p In myDataTable.AsEnumerable()
Select New With {.ID = p.Field(Of Integer)("ID"),
.Name = p.Field(Of String)("Name")}).ToList()
Dim filtered = items.Where(Function(x) x.Name.Contains("Mike")).ToList()
VB
Private Function likes(ByVal dt As DataTable, ByVal column As String, ByVal value As String)
Dim result = dt.Clone()
For Each row As DataRow In From row1 As DataRow In dt.Rows Where (row1(column).Contains(value))
result.ImportRow(row)
Next
Return result
End Function
C#
private DataTable likes(ref DataTable dt, string column, string value)
{
DataTable result = dt.Clone();
foreach (DataRow row in from row1 in dt.Rowswhere (row1(column).Contains(value))) {
result.ImportRow(row);
}
return result;
}

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