LINQ Join multiple datatables - InvalidOperationException - c#

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();

Related

What is the return type of a linq query result from two tables?

I want to return a list of extracted data from two tables into a DataGridView with given column names.
public DataTable GetInfo(int id)
{
var stats = new List<string>();
using MyContext db = new MyContext();
var linqResult = (from Round in db.Rounds
join Hands in db.Hands on Round.RoundID equals Hands.RoundID
where Round.UserID == id
select new
{
ID = Round.RoundID,
Status = Round.Status,
DatePlayed = Round.DatePlayed,
Cards = Hands.PlayerCards,
Score = Hands.PlayerScore
});
return (DataTable)linqResult;
}
The code above returns the correct values but the names of the columns are set to default in the DataGridView. I did some google searching and tried ToString() but I am not able to figure out the correct return type as linqResult is defined as a Generic List with anonymous types. What I want is to avoid naming all the columns manually.
Thank you!
Code below will put your data into a DataTable
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(string));
dt.Columns.Add("Status", typeof(string));
dt.Columns.Add("DatePlayed", typeof(DateTime));
dt.Columns.Add("Cards", typeof(string));
dt.Columns.Add("Score", typeof(int));
foreach (var result in linqResult)
{
dt.Rows.Add(new object[] { result.ID, result.Status, result.DatePlayed, result.Cards, result.Score });
}

Query on DataTable object error is The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'

I have facing some error while select query in Linq.
Error is,
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
Code is below,
dt_TDATA = new DataTable();
dt_TDATA.Columns.Add("MATNR", typeof(string));
dt_TDATA.Columns.Add("MAKTX", typeof(string));
dt_TDATA.Columns.Add("PHY_STK", typeof(Int32));
dt_TDATA.Columns.Add("LABST", typeof(Int32));
DataTable dtDealMat = GetDealMat(); //Get from Store procedure
var MatData = from Data in dt_TDATA.AsEnumerable()
join mat in dtDealMat on Data.Field<string>("MATNR") equals mat.Field<string>("vMATNR")
select new {
MATNR = Data.Field<string>("MATNR"),
MAKTX = Data.Field<string>("MAKTX"),
PHY_STK = Data.Field<string>("PHY_STK"),
LABST = Data.Field<string>("LABST")
};
//GetDealMat() function
private DataTable GetDealMat()
{
clsMOU objMast = new clsMOU();
objMast.vUserID = clsAppSession.SessionUserID;
DataTable dtDealMat = new DataTable();
dtDealMat.Columns.Add("vMATNR", typeof(string));
dtDealMat = objMast.GET_DelMatMapping();
return dtDealMat;
}
Please help in above query..
May be you are not reading second datatable (dtDealMat.AsEnumerable())
var MatData = from Data in dt_TDATA.AsEnumerable()
join mat in dtDealMat.AsEnumerable() on Data.Field<string>("MATNR") equals mat.Field<string>("vMATNR")
select new {
MATNR = Data.Field<string>("MATNR"),
MAKTX = Data.Field<string>("MAKTX"),
PHY_STK = Data.Field<string>("PHY_STK"),
LABST = Data.Field<string>("LABST")
};

How to use Linq to join multiple DataTables on multiple columns containing DBNull values

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

Delete using LINQ by joining two datatables

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;
}

How to populate DataTable with anonymous LINQ result

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);
}

Categories

Resources