How to get Column Names using Linq from DataTable - c#

I'm trying to use LINQ on DataTable that's getting it's data from sql. So I have a data table with it's usual columns and rows and it appears exactly like a sql select statement. Now I need to get certain rows and columns (including column names) from this data.
I converted the datatable to something LINQ can use using AsEnumerable but I'm not sure what exactly it does. Does it convert the data into an array of objects where each row becomes an object?
I'm used to working with Javascript and it's newer arrow functions so i'd like to use Linq with lambda to keep it consistent.
I'm trying to get rows and column names where first column has a value equal to 2018
DataTable myTable = getData(); // populates the datatable and I've verified the data
var linqTable = myTable.AsEnumerable().Select( x => x[0] = 2018);
I need to get the rows and column names. e.g like an object or array of objects.However, the code above doesn't return the data or column names but just two rows with 2018 in it.
My goal is to eventually serialize this data as json and send it to web page.

To Get the column names:
myTable.Columns.Cast<DataColumn>().Select(dc =>dc.ColumnName).ToList();

The problem is Select() is projecting the objects into a new form. You are seeing 2018 because of '=' instead of '=='. You need to use Where()
var linqTable = myTable.AsEnumerable().Where( x => x.Field<int>(0) == 2018);
You will still end up with a list of DataRows though. The DataTable object isn't really what you should be using because it already provides a nice way to filter its rows:
myTable.Rows.Find(2018);
If you are trying to convert it to a list of objects you should use the Select() method something like:
var linqTable = myTable.AsEnumerable().Where(x => x.Field<int>(0) == 2018)
.Select(x => new
{
year = x[0],
p1 = x[1],
p2 = x[2] // etc...
});

You can create the following function:
public static DataTable CreateDataTableFromAnyCollection<T>(IEnumerable<T> list)
{
Type type = typeof(T);
var properties = type.GetProperties();
DataTable dataTable = new DataTable();
foreach (PropertyInfo info in properties)
{
dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
}
foreach (T entity in list)
{
object[] values = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(entity,null);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
and pass any type of object your LINQ query returning.
DataTable dt = CreateDataTableFromAnyCollection(query);
I hope this will help you.
Creating a DataTable From a Query (LINQ to DataSet)

Related

C# Datatable to Dictionary

C#:
I have a datatable with a variable number of columns - returned by calling a stored procedure.
and I would like to convert it to Dictionary key-value pairs (string).
How do I achieve this without having to iterate through each column in each row? I won't know how many columns are there in the datatable at any time.
Thanks
G
You said your result should contain values for the first (and only) row of the dataTable
This one liner can do the trick
var res = dt.Columns.Cast<DataColumn>().ToDictionary(col => col.ColumnName, col=>dt.Rows[0][col]);
ToDictionary will internally enumerate the columns
But I really prefer this explicit enumeration:
var row = dt.Rows[0];
var res2 = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
res2.Add(col.ColumnName, row[col]);
The second is much more readable IMHO.
You can use a method like this calling the extension method ToDictionary() :
internal Dictionary<string,object> GetDict(DataTable dt)
{
return dt.AsEnumerable()
.ToDictionary(row => row.Field<string>(0),
row => row.Field<object>(1));
}

How do Index an Array of type IEnumerable<Data Row>?

I want to index an Array of IEnumerable<DataRow> and print out the Data into a table. I get the below error and I'm not sure hoe to overcome it.
cannot convert type System.Data.DataRow to string
IEnumerable<DataRow> query = from result in
DtSet.Tables["Results"].AsEnumerable()
where result.Field<string
("test").Contains("50")
select result;
var queryArray = query.ToArray();`
for (int i = 0; i < queryArray.Count(); i++)
{
table.Rows[i + 1].Cells[0].Paragraphs.First().Append(queryArray[i]);
}
Consider:
var queryArray = query.ToArray();
(You also have a small problem in that you're trying to stuff a datarow into your destination paragraph; this might just append "system.data.datarow" to your paragraph)
But really you could just delete that line and:
int i = 1:
foreach(var q in query)
table.Rows[i++].Cells[0].Paragraphs.First().Append(q["your column name"].ToString());
That is to say; enumerate the IEnumerable, using a separate indexer variable to keep track of where you are in (the excel sheet?)
Side note; I put a call into extract a single column from the data row; you could alternatively make this a part of your select LINQ statement, converting the datarow to a string enuneabke instead
It looks like using a dataview rowfilter might save you some effort here, something like:
DataView dv = new DataView(DtSet.Tables["Results"]);
dv.RowFilter = "test LIKE '%50%'";
foreach (DataRowView drv in dv)
{
//do the stuff...
}
Microsoft Documentation

How to convert list to datatable using linq [duplicate]

I want to convert my Model data to DataSet or DataTable (to export in excel format)
db.EMPs.ToList()
db is DataContext , EMPs is dataclass .
How to export this list to DataTable, I can export rows to excel but how to access column name from header (column name should not be added manually in DataTable)
You can use ToDataTable extension method but you need to install MoreLinq first. To install MoreLINQ, run the following command in the Package Manager Console:
PM> Install-Package morelinq
Then add the following line to your using directives:
using MoreLinq;
And finally you can use ToDataTable extension method:
DataTable s = db.EMPs.ToDataTable();
To quickly create a file that can be read in Excel you could map the contents to a comma-separated value list using LINQ and then save the array to a file stream.
var records = db.EMP
.ToList()
.Select(record => $"\"{record.stringField}\",{record.numberField}")
.ToArray();
File.WriteAllLines("file.csv", records);
Using MoreLinq is the best way to convert a class to DataTable as already answered by S.Akbari
Below is another way I found to accomplish this by using System.Reflection
List<EMP> list= db.EMPs.ToList();
DataTable dt = new DataTable();
PropertyInfo[] Props = typeof(EMP).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Setting column names as Property names
dt.Columns.Add(prop.Name);
}
foreach (EMP e in list)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(e, null);
}
dt.Rows.Add(values);
}

How can I populate a generic list of string with the results from a single-column DataTable?

I see a lot of complex examples for converting a DataTable with multiple-member rows here but in my case, the query simply returns one column from a table, that is to say 0..N strings/varchars, such as:
bbfinaleofseem#wallacestevens.org
eyenoy#dunbar.com
I thought something like this should work:
DataTable UnitReportPairEmailValsDT = new DataTable();
string qry = string.Format(SQL.UnitReportPairEmailQuery, unit, rptId);
UnitReportPairEmailValsDT = SQL.ExecuteSQLReturnDataTable(
qry,
CommandType.Text,
null
);
List<String> emailAddresses = new List<string>();
foreach (string emailaddr in UnitReportPairEmailValsDT)
{
emailAddresses.Add(emailaddr);
}
...but it won't compile ("foreach statement cannot operate on variables of type 'System.Data.DataTable' because 'System.Data.DataTable' does not contain a public definition for 'GetEnumerator'")
I tried appending ".AsEnumerable" to "in UnitReportPairEmailValsDT" too, but that also provoked the wrath of the compiler.
Error says you cannot loop through DataTable object itself, probably what you need is looping through DataRows.
Use this.
foreach(DataRow row in UnitReportPairEmailValsDT.Rows)
{
emailAddresses.Add(row["emailaddr"].ToString()); // assuming you have emailaddr column.
}
Other option, use Linq
emailAddresses = UnitReportPairEmailValsDT
.AsEnumerable()
.Select(row=> row.Field<string>("emailaddr"))
.ToList();
try something like this:
List<String> emailAddresses = new List<string>();
foreach (DataRow row in UnitReportPairEmailValsDT.Rows)
{
emailAddresses.Add(row.Item(0));
}
Suppose dt is your data table then using Linq:
dt.Rows.Select(x=> x[0]+"").ToList() will give you List. Beware of using Select(x=>xToString()) as it is prone to error if column value is null. x[0]+"" makes sure that in case of null empty string is returned.

Targeting a specific column in a DataRow

I'm trying to perform the C# equivalent of Select * where [columnname] = [value]. I began with a foreach loop to iterate through the table row by row, however I had forgotten that one cannot access a column via row.column["<colname>"].
How do I achieve this objective? Most of the examples I have seen target one specific row with the intention of casting it's value to a string, however my task is to move all entries with a value of DateTime == < DateTime.Today to an archived table.
Can I continue with the following code? Or am I approaching this in the wrong manner?
void archiveDates()
{
foreach (DataRow row in workingupdates.storageTable.Rows)
{
//target DateTime column here
}
}
You can use the Field extension method that is strongly typed and also supports nullable types. You have an overload for the index, name or the DataColumn(among others):
foreach (DataRow row in workingupdates.storageTable.Rows)
{
DateTime dt = row.Field<DateTime>("columnname");
}
If you instead want to find all rows where the date column has a specific value you can use Linq-To-DataTable:
var matchingDataRows = workingupdates.storageTable.AsEnumerable()
.Where(row => row.Field<DateTime>("columnname") == dateTimeVariable);
Now you can simply enumerate this query:
foreach (DataRow row in matchingDataRows)
{
// ...
}
Or create a collection like
a DataRow[] with matchingDataRows.ToArray() or
a List<DataRow> with matchingDataRows.ToList()
a new DataTable with matchingDataRows.CopyToDataTable()
Note that you have to add System.Linq; to the top of the file.

Categories