I have the following LINQ query:
var timesheets = from timesheet in entities.Timesheets
join timesheetTask in entities.Timesheet_Task on timesheet.Id equals timesheetTask.Timesheet_Id
join task in entities.Tasks on timesheetTask.Task_Id equals task.Id
join project in entities.Projects on task.Project_Id equals project.Id
join department in entities.Departments on project.Department_Id equals department.Id
where timesheet.Employee_Id == employeeId
select new
{
date = timesheet.Date,
taskName = task.Name,
projectName = project.Name,
projectDesc = project.Description,
departmentName = department.Name,
taskEstimatedHours = task.Estimated_Hours,
timesheetHours = timesheetTask.Hours
};
How can I put these results into a DataTable which I can then bind to a DataGridView control?
This is what I'm currently doing:
table.Columns.Add("date");
table.Columns.Add("taskName");
table.Columns.Add("projectName");
table.Columns.Add("projectDesc");
table.Columns.Add("departmentName");
table.Columns.Add("taskEstimatedHours");
table.Columns.Add("timesheetHours");
foreach (var item in timesheets)
{
table.Rows.Add(item.date, item.taskName, item.projectName,
item.projectDesc, item.departmentName, item.taskEstimatedHours,
item.timesheetHours);
}
}
Update: Here is my updated code:
DataTable table = new DataTable();
using (PTMS_DataEntities entities = new PTMS_DataEntities())
{
var timesheets = from timesheet in entities.Timesheets
join timesheetTask in entities.Timesheet_Task on timesheet.Id equals timesheetTask.Timesheet_Id
join task in entities.Tasks on timesheetTask.Task_Id equals task.Id
join project in entities.Projects on task.Project_Id equals project.Id
join department in entities.Departments on project.Department_Id equals department.Id
where timesheet.Employee_Id == employeeId
select new
{
date = timesheet.Date,
taskName = task.Name,
projectName = project.Name,
projectDesc = project.Description,
departmentName = department.Name,
taskEstimatedHours = task.Estimated_Hours,
timesheetHours = timesheetTask.Hours
};
table.Columns.Add("date", typeof(DateTime));
table.Columns.Add("taskName", typeof(string));
table.Columns.Add("projectName", typeof(string));
table.Columns.Add("projectDesc", typeof(string));
table.Columns.Add("departmentName", typeof(string));
table.Columns.Add("taskEstimatedHours", typeof(int));
table.Columns.Add("timesheetHours", typeof(int));
List<DataRow> list = new List<DataRow>();
foreach (var item in timesheets)
{
//table.Rows.Add(item.date, item.taskName, item.projectName,
// item.projectDesc, item.departmentName, item.taskEstimatedHours,
// item.timesheetHours);
var row = table.NewRow();
row.SetField<DateTime>("date", item.date);
row.SetField<string>("taskName", item.taskName);
row.SetField<string>("projectName", item.projectName);
row.SetField<string>("projectDesc", item.projectDesc);
row.SetField<string>("departmentName", item.departmentName);
row.SetField<int>("taskEstimatedHours", item.taskEstimatedHours);
row.SetField<int>("timesheetHours", item.timesheetHours);
list.Add(row);
}
table = list.CopyToDataTable();
}
Here is the SQL query I tested in SSMS (which should be the equivalent of the LINQ query):
SELECT dbo.Department.Name, dbo.Task.Name AS Expr1, dbo.Task.Estimated_Hours, dbo.Timesheet.Date, dbo.Project.Name AS Expr2, dbo.Project.Description,
dbo.Timesheet_Task.Date AS Expr3
FROM dbo.Department INNER JOIN
dbo.Project ON dbo.Department.Id = dbo.Project.Department_Id INNER JOIN
dbo.Task ON dbo.Project.Id = dbo.Task.Project_Id INNER JOIN
dbo.Timesheet_Task ON dbo.Task.Id = dbo.Timesheet_Task.Task_Id INNER JOIN
dbo.Timesheet ON dbo.Timesheet_Task.Timesheet_Id = dbo.Timesheet.Id
If you really want to populate DataTable:
// your query
var timesheets = ...
// design table first
DataTable table = new DataTable();
table.Columns.Add(new DataColumn
{
ColumnName = "TaskName",
DataType = typeof(String);
});
...
List<DataRow> list = new List<DataRow>();
foreach (var t in timesheets)
{
var row = table.NewRow();
row.SetField<string>("TaskName", t.taskName); // extension method from System.Data.DataSetExtensions.dll
...
list.Add(row);
}
DataTable table = list.CopyToDataTable(); // extension method too
Or more LINQ way:
timesheets
.Select(t =>
{
var row = table.NewRow();
...
return row;
})
.CopyToDataTable();
Or in same query syntax. Implement a method:
static DataRow NewRow(DataRow row, string taskName, ....)
{
...
}
Then query itself:
(from ...
where ...
select NewRow(table.NewRow(), task.Name, ...)
).CopyToDataTable();
Call .ToList().
The resulting List<T> can also be bound to a DataGridView, and is easier to work with than a DataTable.
I'm using FastMember for this purpose. It uses IL instead of reflection (much faster) to iterate over all of the property and field values automatically. Code sample from the site:
IEnumerable<SomeType> data = ...
var table = new DataTable();
using(var reader = ObjectReader.Create(data))
{
table.Load(reader);
}
Related
I have 3 DataTable objects with relational data that I want to join.
The first has a schema that looks like
DataTable parent = new DataTable();
parent.Columns.Add("Id1", typeof(string));
parent.Columns.Add("Id2", typeof(string));
// more metadata
Where either of the Ids may be DBNull, but not both.
The two chlid tables have the same schema.
DataTable child = new DataTable();
child.Columns.Add("Id1", typeof(string));
child.Columns.Add("Id2", typeof(string));
child.Columns.Add("BeginDate", typeof(DateTime));
child.Columns.Add("SomeData", typeof(float));
// more data
I have a function that takes all three DataTables as inputs, and is supposed to return the joined table.
var dataToReturn = // will be converted to a DataTable later.
from
p in parent.AsEnumerable()
join c1 in child1.AsEnumerable()
on new {
Id1 = p["Id1"],
Id2 = p["Id2"],
}
equals new {
Id1 = c1["Id1"],
Id2 = c1["Id2"],
}
join c2 in child2.AsEnumerable()
on new {
Id1 = p["Id1"],
Id2 = p["Id2"],
BeginDate = c1["BeginDate"],
}
equals new {
Id1 = p["Id1"],
Id2 = p["Id2"],
BeginDate = c1["BeginDate"],
}
select new {
Id1 = p["Id1"],
Id2 = p["Id2"],
BeginDate = c1["BeginDate"],
Child1Data = c1["SomeData"],
Child2Data = c2["SomeData"],
}
However, this returns no results, even though there are many non-null values matching on those conditions.
If I were writing SQL (with "is not distinct from" from Postgres to make null = null return true for brevity), I would write
select
p.Id1,
p.Id2,
c1.BeginDate,
c1.SomeData as Child1Data,
c2.SomeData as Child2Data
from
Parent p
join Child1 c1
on c1.Id1 is not distinct from p.Id1
and c1.Id2 is not distinct from p.Id2
join Child2 c2
on c2.Id1 is not distinct from p.Id1
and c2.Id2 is not distinct from p.Id2
and c2.BeginDate = c1.BeginDate
Notice the use of "is not distinct from" over "=" for the Id fields, since I want "DBNull.Value == DBNull.Value" to return true.
My questions:
Is this possible to do with in-memory DataTables with Linq?
How does Linq handle DBNull.Value comparisons in these queries?
Linq will match DBNull.Value to another DBNull.Value correctly. I believe that was your question and not joining a second child table as this is trivial
DataTable parent = new DataTable();
parent.Columns.Add("Id1", typeof(string));
parent.Columns.Add("Id2", typeof(string));
DataRow r1 = parent.NewRow();
r1["Id1"] = "1";
r1["Id2"] = DBNull.Value;
parent.Rows.Add(r1);
DataRow r3 = parent.NewRow();
r3["Id1"] = "1";
r3["Id2"] = "2";
parent.Rows.Add(r3);
DataTable child1 = new DataTable();
child1.Columns.Add("Id1", typeof(string));
child1.Columns.Add("Id2", typeof(string));
child1.Columns.Add("BeginDate", typeof(DateTime));
child1.Columns.Add("SomeData", typeof(float));
DataRow r2 = child1.NewRow();
r2["Id1"] = "1";
r2["Id2"] = DBNull.Value;
child1.Rows.Add(r2);
DataRow r4 = child1.NewRow();
r4["Id1"] = "1";
r4["Id2"] = "2";
child1.Rows.Add(r4);
var dataToReturn =
from
p in parent.AsEnumerable()
join c1 in child1.AsEnumerable()
on new { Id1 = p["Id1"], Id2 = p["Id2"] }
equals new { Id1 = c1["Id1"], Id2 = c1["Id2"] }
select new
{
Id1 = p["Id1"],
Id2 = p["Id2"]
};
foreach(var l in dataToReturn)
{
Console.WriteLine(l.Id1 + "|" + l.Id2);
}
Console.ReadKey();
Output:
1|
1|2
I have two datatable DurationByCurrency(inside a dataset) and Fund which looks like below
I want to delete the rows in Duration By Currency Datatable whose FundCode has value as 2 in Fund Dt by performing a join.
var result = from table1 in raptorDS.Tables[RaptorTable.DurationByCurrency].AsEnumerable()
join table2 in fundDT.AsEnumerable()
on table1.Field<string>("FundCode") equals table2.Field<string>("FundCode") into ps
from row in ps.DefaultIfEmpty()
{
//delete query
}
Please help me on this as I am new to LINQ.
var result = from row1 in raptorDS.Tables[RaptorTable.DurationByCurrency].AsEnumerable()
join row2 in fundDT.AsEnumerable()
on row1.Field<string>("FundCode") equals row2.Field<string>("FundCode")
where row1.Field<string>("value")
equals "2" select row1;
result.ToList().ForEach(row => row.Delete());
sample test code for linqpad:
void Main()
{
//sample data for test
DataSet ds = new DataSet();
ds.Tables.Add(GetTable1());
ds.Tables.Add(GetTable2());
var result = ( from rec1 in ds.Tables[0].AsEnumerable()
join rec2 in ds.Tables[1].AsEnumerable()
on rec1.Field<string>("FC") equals rec2.Field<string>("FC")
where rec2.Field<int>("Value") == 2 select rec1);
result.ToList().ForEach(row => row.Delete());
//now you have only "ABCD" and "AZY" in table 1
//ds.Tables[0].Dump(); linqpad display result
}
DataTable GetTable1()
{
DataTable table = new DataTable();
table.Columns.Add("FC", typeof(string));
table.Rows.Add("ABCD");
table.Rows.Add("XYZ");
table.Rows.Add("AZY");
return table;
}
DataTable GetTable2()
{
DataTable table = new DataTable();
table.Columns.Add("FC", typeof(string));
table.Columns.Add("Value", typeof(int));
table.Rows.Add("ABCD", 1);
table.Rows.Add("XYZ", 2);
table.Rows.Add("AZY",3);
return table;
}
I've got three tables of Datatable type:
Table1: Id<Int64>, ParamX<string>;
Table2: Id<Int64>;
Table3: Id<Int64>, ParamA<double>, ParamB<Int16>;
I want to Join them by ID field and create Table4 with calculated field ParamC<double>=ParamA<double>*ParamB<Int16>
The result should be datatable:
Table4: Id<Int64>, ParamX<string>, ParamA<double>, ParamB<Int16>, ParamC<double>
Is it possible to make it in single LINQ query?
madace
You cannot create or fill tables in a LINQ query, a query is not supposed to cause side-effects. You can prepare the data you want to insert into the new table with LINQ:
var query = from r1 in Table1.AsEnumerable()
join r2 in Table2.AsEnumerable()
on r1.Field<long>("ID") equals r2.Field<long>("ID")
join r3 in Table3.AsEnumerable()
on r2.Field<long>("ID") equals r3.Field<long>("ID")
select new {
ID = r1.Field<long>("ID"),
ParamX = r1.Field<string>("ParamX"),
ParamA = r3.Field<double>("ParamA"),
ParamB = r3.Field<short>("ParamB"),
ParamC = r3.Field<double>("ParamA") * r3.Field<short>("ParamB")
};
var Table4 = new DataTable();
Table4.Columns.Add("ID", typeof(long));
Table4.Columns.Add("ParamX", typeof(string));
Table4.Columns.Add("ParamA", typeof(double));
Table4.Columns.Add("ParamB", typeof(short));
Table4.Columns.Add("ParamC", typeof(double));
You use a loop to execute the query and to insert the rows into the table:
foreach (var x in query)
{
DataRow addedRow = Table4.Rows.Add();
addedRow.SetField("ID", x.ID);
addedRow.SetField("ParamX", x.ParamX);
addedRow.SetField("ParamA", x.ParamA);
addedRow.SetField("ParamB", x.ParamB);
addedRow.SetField("ParamC", x.ParamC);
}
For what it's worth, here's an extension method that allows to create a DataTable from an IEnumerable<anything> via reflection:
public static class Extensions
{
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}
}
Then you don't need to create or fill the table manually:
DataTable Table4 = query.ToDataTable();
I have below code which I use to join multiple datatables using LINQ. The code has no errors, but it return a runtime error/InvalidOperationException saying "The source contains no DataRows."
If I comment from line where it says from join raw3 to end of join raw4.Field("OtherString") }, I see the data in DataTable after CopyToDataTable statement.
If I use all the datatable, I am getting InvalidOperationException "The source contains no DataRows." error. Also, I want to join all the tables with first table (firstDT).
DataTable datatable = new DataTable();
datatable.Columns.Add("First_Code", typeof(string));
datatable.Columns.Add("First_Date", typeof(DateTime));
datatable.Columns.Add("OtherString", typeof(string));
datatable.Columns.Add("FirstPrice", typeof(decimal));
datatable.Columns.Add("SecondPrice", typeof(decimal));
datatable.Columns.Add("ThirdPrice", typeof(decimal));
datatable.Columns.Add("FourthPrice", typeof(decimal));
var data = from raw1 in firstDT.AsEnumerable()
join raw2 in secondDT.AsEnumerable() on
new { ID = raw1.Field<string>("First_Code"), Date = raw1.Field<DateTime>("First_Date"), OtherString = raw1.Field<string>("OtherString") }
equals
new { ID = raw2.Field<string>("Second_Code"), Date = raw2.Field<DateTime>("Second_Date"), OtherString = raw2.Field<string>("OtherString") }
join raw3 in thirdDT.AsEnumerable() on
new { ID = raw1.Field<string>("First_Code"), Date = raw1.Field<DateTime>("First_Date"), OtherString = raw1.Field<string>("OtherString") }
equals
new { ID = raw3.Field<string>("Third_Code"), Date = raw3.Field<DateTime>("Third_Date"), OtherString = raw3.Field<string>("OtherString") }
join raw4 in fourthDT.AsEnumerable() on
new { ID = raw1.Field<string>("First_Code"), Date = raw1.Field<DateTime>("First_Date"), OtherString = raw1.Field<string>("OtherString") }
equals
new { ID = raw4.Field<string>("Fourth_Code"), Date = raw4.Field<DateTime>("Fourth_Date"), OtherString = raw4.Field<string>("OtherString") }
select datatable.LoadDataRow(new object[]
{
raw1.Field<string>("First_Code"),
raw1.Field<DateTime>("First_Date"),
raw1.Field<string>("OtherString"),
raw1.Field<decimal>("FirstPrice"),
raw2.Field<decimal>("SecondPrice")
raw3.Field<decimal>("ThirdPrice")
raw4.Field<decimal>("FourthPrice")
}, false);
data.CopyToDataTable();
My linq is like below
from dt1 in dsResults.Tables[0].AsEnumerable()
join dt2 in dsResults.Tables[1].AsEnumerable()
on dt1 .Field<decimal>("RecordId") equals dt2.Field<decimal>("RecordId2")
select dt1 ;
This will retrieve only columns of dt1 . How can i make columns of both table appear ?
I could do this as below follwing the thread
Create combined DataTable from two DataTables joined with LINQ. C#
DataTable targetTable = dsResults.Tables[0].Clone();
var dt2Columns = dsResults.Tables[1].Columns.OfType<DataColumn>().Select(dc =>
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
targetTable.Columns.AddRange(dt2Columns.ToArray());
var rowData =
from row1 in dsResults.Tables[0].AsEnumerable()
join row2 in dsResults.Tables[1].AsEnumerable()
on row1.Field<decimal>("RecordId") equals row2.Field<decimal>("RecordId2")
select row1.ItemArray.Concat(row2.ItemArray).ToArray();
foreach (object[] values in rowData)
targetTable.Rows.Add(values);
you could create an object representing the columns you want to show then you can do the below:
public class YourObject
{
public string Pror1 {get;set;}
public string Pror2 {get;set;}
}
List<YourObject> result=from row1 in dsResults.Tables[0].AsEnumerable()
join row2 in dsResults.Tables[1].AsEnumerable()
on row1.Field<decimal>("RecordId") equals row2.Field<decimal>("RecordId2")
select new YourObject()
{
Pror1=row1.prop1,
Prop2=row2.prop2,
......
}.ToList();
if you want to select all the column you can do the below
from row1 in dsResults.Tables[0].AsEnumerable()
join row2 in dsResults.Tables[1].AsEnumerable()
on row1.Field<decimal>("RecordId") equals row2.Field<decimal>("RecordId2")
select new { RowTable1 = row1, RowTable2 = row2}; //anonymous type
In this case each element of the result will have two properties: RowTable1 will be a row from row1, and RowTable2 will be the matching row from row2
1 You can adjust you select clause with anonymous type , and inclue your needed columns with Field properties
from dt1 in dsResults.Tables[0].AsEnumerable()
join dt2 in dsResults.Tables[1].AsEnumerable()
on dt1 .Field<decimal>("RecordId") equals dt2.Field<decimal>("RecordId2")
select new
{
Property1 = dt1 .Field<decimal>("RecordId"),
Property2 = dt2 .Field<decimal>("RecordId2")
......
}
;
2 If you don't want specify column name
from dt1 in dsResults.Tables[0].AsEnumerable()
join dt2 in dsResults.Tables[1].AsEnumerable()
on dt1 .Field<decimal>("RecordId") equals dt2.Field<decimal>("RecordId2")
select new
{
dt1,
dt2
}
;
3 If you want set Union between your sequence
Link : http://msdn.microsoft.com/en-us/library/bb386993.aspx
public string ShowNew(string myName)
{
RENTALEntities objD = new RENTALEntities();
var NewObj =
(from t in objD.TENANTs.ToList()
join pt in objD.PROP_TYPE.ToList()
on t.Prop_ID equals pt.Prop_ID
where t.Surname == myName
select new { t.Surname, t.PropAdress, pt.DESCRIPTION, t.RentalIncome }).First();
return "SWurname" + NewObj.Surname + "<br/>"
+ "Address" + NewObj.PropAdress + "<br/>"
+ "Description" + NewObj.DESCRIPTION + "<br/>"
+ "Rental income" + NewObj.RentalIncome;
}