Reputation: 475
I have this linq
query:
var lastDaysData = from myRow in rawDataSet.AsEnumerable()
where myRow.Field<DateTime>("DateTime") < DateTime.Now.AddHours(-24)
select myRow;
which i think should return the last 24 hrs worth of data from the SQL database.
i am trying to add the results of the query to a datatable
with a foreach
loop:
DataTable dataTable = new DataTable();
foreach (DataRow row in lastDaysData)
{
dataTable.ImportRow(row);
}
dataGridView1.DataSource = dataTable;
However, my datagridview
is not populating with any data.
So am i adding the rows incorrectly to the datatable
or is my linq query wrong?
Upvotes: 0
Views: 1918
Reputation: 26926
Most of the time, I would say you shouldn't use DataTable
s in modern C#, you should use EF or LINQ to SQL and use objects directly.
But when I must work with them, I use some extension methods to make using LINQ with DataTable
s a bit easier.
This one converts the resulting IEnumerable<DataRow>
from a LINQ query on a DataTable.AsEnumerable()
back to a DataTable
with a matching schema:
public static DataTable ToDataTable(this IEnumerable<DataRow> src) {
var ans = src.First().Table.Clone();
foreach (var r in src)
ans.ImportRow(r);
return ans;
}
This one let's you use anonymous objects to select out columns and then creates a new DataTable
from the objects, but it uses reflection and so can be slow. It may be better to make a variation of the above ToDataTable
that takes a list of column names projects the answer accordingly.
public static DataTable ToDataTable<T>(this IEnumerable<T> rows) {
var dt = new DataTable();
if (rows.Any()) {
var rowType = typeof(T);
var memberInfos = rowType.GetPropertiesOrFields();
foreach (var info in memberInfos)
dt.Columns.Add(new DataColumn(info.Name, info.GetMemberType()));
foreach (var r in rows)
dt.Rows.Add(memberInfos.Select(i => i.GetValue(r)).ToArray());
}
return dt;
}
You need some MemberInfo
extensions to make working with properties and fields easier:
// ***
// *** Type Extensions
// ***
public static List<MemberInfo> GetPropertiesOrFields(this Type t, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
t.GetMembers(bf).Where(mi => mi.MemberType == MemberTypes.Field | mi.MemberType == MemberTypes.Property).ToList();
// ***
// *** MemberInfo Extensions
// ***
public static Type GetMemberType(this MemberInfo member) {
switch (member) {
case FieldInfo mfi:
return mfi.FieldType;
case PropertyInfo mpi:
return mpi.PropertyType;
case EventInfo mei:
return mei.EventHandlerType;
default:
throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", nameof(member));
}
}
public static object GetValue(this MemberInfo member, object srcObject) {
switch (member) {
case FieldInfo mfi:
return mfi.GetValue(srcObject);
case PropertyInfo mpi:
return mpi.GetValue(srcObject);
default:
throw new ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", nameof(member));
}
}
public static T GetValue<T>(this MemberInfo member, object srcObject) => (T)member.GetValue(srcObject);
Finally, if you do a join across multiple DataTable
s, you can flatten the answer back to another DataTable
with this method:
// Create new DataTable from LINQ results on DataTable
// Expect T to be anonymous object of form new { DataRow d1, DataRow d2, ... }
public static DataTable FlattenToDataTable<T>(this IEnumerable<T> src) {
var res = new DataTable();
if (src.Any()) {
var firstRow = src.First();
var rowType = typeof(T);
var memberInfos = rowType.GetPropertiesOrFields();
var allDC = memberInfos.SelectMany(mi => mi.GetValue<DataRow>(firstRow).Table.DataColumns());
foreach (var dc in allDC) {
var newColumnName = dc.ColumnName;
if (res.ColumnNames().Contains(newColumnName)) {
var suffixNumber = 1;
while (res.ColumnNames().Contains($"{newColumnName}.{suffixNumber}"))
++suffixNumber;
newColumnName = $"{newColumnName}.{suffixNumber}";
}
res.Columns.Add(new DataColumn(newColumnName, dc.DataType));
}
foreach (var objRows in src)
res.Rows.Add(memberInfos.SelectMany(mi => mi.GetValue<DataRow>(objRows).ItemArray).ToArray());
}
return res;
}
Upvotes: 1
Reputation: 475
I figured it out myself in the end.
I had to add the columns i wanted to a List
first, then add the list to a dataTable
var rawDataSet = pDCDataSet.RawData;
var lastDaysData = from myRow in rawDataSet.AsEnumerable()
where myRow.Field<DateTime>("DateTime") > DateTime.Now.AddHours(-Convert.ToInt32(comboBox1.Text))
select new
{
myRow.DateTime,
myRow.FolderSize,
myRow.FileNumber,
//myRow.Results,
};
var newlist = lastDaysData.ToList();
DataTable dt = new DataTable();
dt.Columns.Add("DateTime");
dt.Columns.Add("FolderSize");
dt.Columns.Add("FileNumber");
foreach (var item in newlist)
{
var row = dt.NewRow();
row["DateTime"] = item.DateTime;
row["FolderSize"] = Convert.ToString(item.FolderSize);
row["FileNumber"] = item.FileNumber;
dt.Rows.Add(row);
}
dataGridView1.DataSource = dt;
Upvotes: 0