LINQ and NOT IN Clause - c#

I have 2 datatables one is a lookup list the other is the data. I have many columns columns in the table holding the data and one of these columns is the foreign key column to the lookup datatable.
I want to produce another list for all the lookup rows which do NOT appear in the table holding the data based on the the Id on the foreign key column.
I want to use a linq query, or something that works on the 2 datatables
As if I was doing a SQL NOT IN.
Cheers
Here is some data. I expect the new list to have category 4 and 5 in it.
DataTable dtData = new DataTable( "Data" );
dtData.Columns.Add( "Id", Type.GetType( "System.Int32" ) );
dtData.Columns.Add( "CategoryId", Type.GetType( "System.Int32" ) );
dtData.Columns.Add( "Qty", Type.GetType( "System.Int32" ) );
dtData.Columns.Add( "Cost", Type.GetType( "System.Decimal" ) );
dtData.Columns.Add( "TotalCost", Type.GetType( "System.Decimal" ) );
dtData.Columns.Add( "TypeId", Type.GetType( "System.Int32" ) );
dtData.Rows.Clear();
DataRow row = dtData.NewRow();
row["Id"] = 1;
row["CategoryId"] = 1;
row["Qty"] = 3;
row["Cost"] = 237.00;
row["TotalCost"] = 711.00;
row["TypeId"] = DBNull.Value;
dtData.Rows.Add( row );
row = dtData.NewRow();
row["Id"] = 2;
row["CategoryId"] = 1;
row["Qty"] = 5;
row["Cost"] = 45.00;
row["TotalCost"] = 225.00;
row["TypeId"] = DBNull.Value;
dtData.Rows.Add( row );
row = dtData.NewRow();
row["Id"] = 3;
row["CategoryId"] = 3;
row["Qty"] = 30;
row["Cost"] = 1.00;
row["TotalCost"] = 30.00;
row["TypeId"] = 1;
dtData.Rows.Add( row );
row = dtData.NewRow();
row["Id"] = 4;
row["CategoryId"] = 2;
row["Qty"] = 1;
row["Cost"] = 15.00;
row["TotalCost"] = 15.00;
row["TypeId"] = 2;
dtData.Rows.Add( row );
row = dtData.NewRow();
row["Id"] = 5;
row["CategoryId"] = 1;
row["Qty"] = 4;
row["Cost"] = 3.00;
row["TotalCost"] = 12.00;
row["TypeId"] = 2;
dtData.Rows.Add( row );
DataTable dtlookup = new DataTable( "LookUp" );
dtlookup.Columns.Add( "CategoryId", Type.GetType( "System.Int32" ) );
dtlookup.Columns.Add( "Description", Type.GetType( "System.String" ) );
dtlookup.Rows.Clear();
DataRow lup = dtlookup.NewRow();
lup["CategoryId"] = 1;
lup["Description"] = "Category 1";
dtlookup.Rows.Add( lup );
lup = dtlookup.NewRow();
lup["CategoryId"] = 2;
lup["Description"] = "Category 2";
dtlookup.Rows.Add( lup );
lup = dtlookup.NewRow();
lup["CategoryId"] = 3;
lup["Description"] = "Category 3";
dtlookup.Rows.Add( lup );
lup = dtlookup.NewRow();
lup["CategoryId"] = 4;
lup["Description"] = "Category 4";
dtlookup.Rows.Add( lup );
lup = dtlookup.NewRow();
lup["CategoryId"] = 5;
lup["Description"] = "Category 5";
dtlookup.Rows.Add( lup );
var qqq = ( from r in dtlookup.AsEnumerable()
where !dtData.AsEnumerable().Any( b => b["CategoryId"] == r["CategoryId"] )
select r ).ToList();

I suspect it was dahlbyk's answer that helped you, so I'll paste that here.
Linq not in select on datatable
Except would work if you use it on sequences of the countries:
using System.Linq;
...
var ccList = from c in ds.Tables[2].AsEnumerable()
select c.Field<string>("Country");
var bannedCCList = from c in ds.Tables[1].AsEnumerable()
select c.Field<string>("Country");
var exceptBanned = ccList.Except(bannedCCList);
If you need the full rows where the countries aren't banned, you could try a left outer join:
var ccList = ds.Tables[2].AsEnumerable();
var bannedCCList = ds.Tables[1].AsEnumerable();
var exceptBanned = from c in ccList
join b in bannedCCList
on c.Field<string>("Country") equals b.Field<string>("Country") into j
from x in j.DefaultIfEmpty()
where x == null
select c;

You can use the DataTable.AsEnumerable() extension method to return an enumerable collection of your data. From there you can use LINQ to effectively query your data.
var myData = myDataTable.AsEnumerable()
Edit: If you need to determine if an ID is contained in your data table, you will have to select out the list of IDs first (since you cannot compare entire DataRow objects).
bool idExists = dtData
.AsEnumerable()
.Select(item => (int) item["Id"])
.Contains(myId);

Related

Group and Sum DataTable

I have Data Table with the following data
Number Type Order count
1 1 R 1
1 1 R 1
1 1 R 1
1 2 R 1
I am looking to get to this result
Number Type Order count
1 1 R 3
1 2 R 1
How can I group by three columns
var result = dt.AsEnumerable()
.GroupBy(x => {x.Field<string>("Number"))//need to group by Type and order also need to sum te total counts
rgoal
Your question made me curious, so I did some digging on Stack Overflow.
esc's answer appears will also solve your issue. It is posted under: How do I use SELECT GROUP BY in DataTable.Select(Expression)?:
Applying his method to your problem gave me this solution:
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
This will return a DataTable matching the schema of the input DataTable with the grouping and counts you requested.
Here is the full code I use to verify in LINQPad:
DataTable dt = new DataTable("Demo");
dt.Columns.AddRange
(
new DataColumn[]
{
new DataColumn ( "Number", typeof ( int ) ),
new DataColumn ( "Type", typeof ( int ) ),
new DataColumn ( "Order", typeof ( string ) ),
new DataColumn ( "Count", typeof ( int ) )
}
);
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,2,"R", 1 });
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
foreach (DataRow row in dt2.Rows)
{
for (int i = 0; i < dt2.Columns.Count; i++)
Console.Write("{0}{1}",
row[i], // Print column data
(i < dt2.Columns.Count - 1)? " " : Environment.NewLine); // Print column or row separator
}
Here are the results:
1 1 R 3
1 2 R 1

Compare two DataTables and Display the differences in another datatable in LINQ

I have two DataTables and I want to display the rows. if both the datatables having the same value, Then mark X in all columns or else select the column with highest value(Eg:DT1: 10,DT2 :5)
Datatable1
id Name Weight
1 Ship 500
2 Train 600
3 Plane 700
4 Car 800
Datatable2
id Name Weight
1 Ship 500
3 Plane 600
4 Car 200
I want the result to be:
Datatable3
id Name Weight Datatable1 Datatable2
1 Ship 500 X X
2 Train 600 X
3 Plane 700 X X
4 Car 800 X
I have tried the below:-
DataTable Datatable3 = (from a in Datatable1.AsEnumerable()
join b in Datatable2.AsEnumerable()
on a["Name"].ToString() equals b["Name"].ToString()
a["Weight"].ToString() equals b["Weight"].ToString() into g
where g.Count() != 1 select a).CopyToDataTable();
dataGrid1.ItemsSource = Datatable3.DefaultView;
Please help me on this. Thanks in advance
This is what I have:-
DataTable Datatable3 = dt1.AsEnumerable().Union(dt2.AsEnumerable())
.GroupBy(x => x.Field<int>("Id"))
.Select(x =>
{
var topWeightItem = x.OrderByDescending(z => z.Field<int> ("Weight")).First();
return new Items
{
Id = x.Key,
Name = topWeightItem.Field<string>("Name"),
Weight = topWeightItem.Field<int>("Weight"),
DataTable1 = dt1.AsEnumerable().Any(z => z.Field<int>("Id") == x.Key
&& z.Field<int>("Weight") == topWeightItem.Field<int>("Weight")
&& z.Field<string>("Name") == topWeightItem.Field<string>("Name"))
? "X" : String.Empty,
DataTable2 = dt2.AsEnumerable().Any(z => z.Field<int>("Id") == x.Key
&& z.Field<int>("Weight") == topWeightItem.Field<int>("Weight")
&& z.Field<string>("Name") == topWeightItem.Field<string>("Name"))
? "X" : String.Empty
};
}
).PropertiesToDataTable<Items>();
Since It is returning an anonymous type, you can't use CopyToDataTable method, so please check this to understand how I converted it into a datatable.
I am getting this output:-
I have used following type for conversion purpose:-
public class Items
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Weight { get; set; }
public string DataTable1 { get; set; }
public string DataTable2 { get; set; }
}
I have written in two query to achieve what you said..Perhaps you can optimize it further.
//datatable1
DataTable dt1 = new DataTable();
dt1.Columns.Add("Id");
dt1.Columns.Add("Name");
dt1.Columns.Add("Weight");
DataRow dr ;
dr = dt1.NewRow();
dr["Id"] = 1;
dr["Name"] = "Ship";
dr["Weight"] = 500;
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["Id"] = 2;
dr["Name"] = "Train";
dr["Weight"] = 600;
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["Id"] = 3;
dr["Name"] = "Plane";
dr["Weight"] = 700;
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["Id"] = 4;
dr["Name"] = "Car";
dr["Weight"] = 400;
dt1.Rows.Add(dr);
//datatable2
DataTable dt2 = new DataTable();
dt2.Columns.Add("Id");
dt2.Columns.Add("Name");
dt2.Columns.Add("Weight");
DataRow dr2;
dr2 = dt2.NewRow();
dr2["Id"] = 1;
dr2["Name"] = "Ship";
dr2["Weight"] = 500;
dt2.Rows.Add(dr2);
dr2 = dt2.NewRow();
dr2["Id"] = 3;
dr2["Name"] = "Plane";
dr2["Weight"] = 700;
dt2.Rows.Add(dr2);
dr2 = dt2.NewRow();
dr2["Id"] = 4;
dr2["Name"] = "Car";
dr2["Weight"] = 400;
dt2.Rows.Add(dr2);
//iterate through table1
IEnumerable<DataRow> table1 = from r in dt1.AsEnumerable()
select r;
//iterate through table2
IEnumerable<DataRow> table2 = from r in dt2.AsEnumerable()
select r;
Console.WriteLine("Id\tName\tWeight\tDatatable1\tDatatable2");
Console.WriteLine("----------------------------------------------------");
//prints the common records
foreach (DataRow td1 in table1.Distinct())//Matches wholes of the Element Sequence inside IEnumerable
{
table2.Distinct().ToList().ForEach(td2 =>
{
if (td1.Field<string>("Id") == td2.Field<string>("Id"))
{
Console.WriteLine(td1.Field<string>("Id") + "\t" + td1.Field<string>("Name") + "\t" + td1.Field<string>("Weight") + "\t" + "x" + "\t\t" + "x");
}
});
}
//prints the missing records
var query = (from tb1 in dt1.AsEnumerable()
join tb2 in dt2.AsEnumerable()
on tb1.Field<string>("Id") equals tb2.Field<string>("Id") into subset
from sc in subset.DefaultIfEmpty()
where sc == null
select new
{
id = tb1.Field<string>("Id"),
name = tb1.Field<string>("Name"),
wt = tb1.Field<string>("Weight")
}).Distinct();
foreach (var td1 in query)
{
Console.WriteLine(td1.id + "\t" + td1.name + "\t" + td1.wt + "\t" + "x" + "\t\t" + "-");
}

Select() using and operator doesnt returns a value similar to or operator when querying datatable C#

I am trying to select records in which the rows have both the "ID" field is equal to the "id" value value and the "Date" field is equal to the date field "Log Date" in a different table "dv".
The problem is that the returned records in "foundID" are not processing the rows in AND manner but it is processing it as if it was OR.
for (int id = 0; id < 200; id++)
{
var foundID = from r in dv.AsEnumerable()
orderby r.Field<string>("Log Date")
where r.Field<int>("User ID") == id
select r;
foreach (var row in foundID)
{
var foundDate = overtime.Select("ID = '" + id
+ "' AND 'Log Date' = '"+row.Field<string>("Log Date")+"'");
if(foundDate.Count() == 0){
Rowq["ID"] = row.Field<int>("User ID");
Rowq["Date"] = row.Field<string>("Log Date");
overtime.Rows.Add(Rowq);
Rowq = overtime.NewRow();
}
else {
continue;
}
}
id++;
}
the input "dv" table looks like this:
User ID Name Log Date Log Time FKey
0000000002 Name1 2014/10/16 09:03:13 F1
0000000002 Name1 2014/10/16 17:02:20 F2
0000000002 Name1 2014/10/18 08:38:42 F1
0000000002 Name1 2014/10/18 16:55:02 F2
0000000002 Name1 2014/10/19 09:05:21 F1
0000000004 Name2 2014/10/01 00:07:09 F2
0000000004 Name2 2014/10/01 15:46:49 F1
0000000004 Name2 2014/10/02 00:09:52 F2
the output "overtime" must look like this:
ID Name Date F1 F2 None
2 Name1 2014/10/18 time time time
2 Name1 2014/10/19 time time time
4 Name2 2014/10/01 time time time
4 Name2 2014/10/01 time time time
4 Name2 2014/10/02 time time time
etc..
As you can notice, each ID has several records one for each date. but the output table "overtime" returns two records for each ID with one random date.
I have fixed a problem in the first post about the increment but still it now displays two rows for each ID. I want to merge each row with its counter part rows of the same ID and Date. the function of
var foundDate = overtime.Select("ID = '" + id
+ "' AND 'Log Date' = '"+row.Field("Log Date")+"'");
doesn't return a record although the overtime table contains a matching record of ID and Date
Firstly, it should be
var foundDate = overtime.Select("ID = '" + id
+ "' AND [Log Date] = '"+row.Field<string>("Log Date")+"'");
Note that [Log Date] instead of 'Log Date'
Secondly, you're incrementing id after logging an overtime, but then continue inserting rows explicitly matching the previous id.
Either, the incrementation is wrong, or you should break out of the loop after incrementing.
Take a look at the following, it provides the correct data in the overtime table
void Main()
{
var overtime = new DataTable();
overtime.Columns.Add("ID", typeof(int));
overtime.Columns.Add("Log Date", typeof(string));
overtime.Columns.Add("F1", typeof(string));
overtime.Columns.Add("F2", typeof(string));
var dv = new DataTable();
dv.Columns.Add("User ID", typeof(int));
dv.Columns.Add("Log Date", typeof(string));
dv.Columns.Add("Type", typeof(string));
dv.Columns.Add("Time", typeof(string));
var row = dv.NewRow();
row["User ID"] = 0000000002;
row["Log Date"] = "2014/10/16";
row["Type"] = "F1";
row["Time"] = "09:03:13";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000002;
row["Log Date"] = "2014/10/16";
row["Type"] = "F2";
row["Time"] = "17:02:20";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000002;
row["Log Date"] = "2014/10/18";
row["Type"] = "F1";
row["Time"] = "08:38:42";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000002;
row["Log Date"] = "2014/10/19";
row["Type"] = "F2";
row["Time"] = "16:55:02";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000002;
row["Log Date"] = "2014/10/19";
row["Type"] = "F1";
row["Time"] = "09:05:21";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000004;
row["Log Date"] = "2014/10/01";
row["Type"] = "F2";
row["Time"] = "00:07:09";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000004;
row["Log Date"] = "2014/10/01";
row["Type"] = "F1";
row["Time"] = "15:46:49";
dv.Rows.Add(row);
row = dv.NewRow();
row["User ID"] = 0000000004;
row["Log Date"] = "2014/10/02";
row["Type"] = "F2";
row["Time"] = "00:09:52 ";
dv.Rows.Add(row);
foreach(var personGrouping in dv.Rows.Cast<DataRow>().GroupBy(r => r.Field<int>("User ID")))
{
foreach(var dayGrouping in personGrouping.GroupBy(r => r.Field<string>("Log Date")))
{
//Now we need to find F1 and F2
var orderedTime = dayGrouping.OrderBy(dg => dg.Field<string>("Time")).ToList();
for (var i = 0; i < orderedTime.Count; i++)
{
var f1 = orderedTime[i];
if (orderedTime.Count <= i + 1) //Either F2 doesn't exist, or it exists on another date
continue;
var f2 = orderedTime[i + 1];
if (!overtime.Rows.Cast<DataRow>().Any (dr => dr.Field<int>("ID") == personGrouping.Key && dr.Field<string>("Log Date") == dayGrouping.Key))
{
row = overtime.NewRow();
row["ID"] = personGrouping.Key;
row["Log Date"] = dayGrouping.Key;
row["F1"] = f1.Field<string>("Time");
row["F2"] = f2.Field<string>("Time");
overtime.Rows.Add(row);
}
}
}
}
overtime.Dump();
}
This produces the output:
2 2014/10/16 09:03:13 17:02:20
2 2014/10/19 09:05:21 16:55:02
4 2014/10/01 00:07:09 15:46:49
If F2 falls on the next day (which happens in your example data):
0000000002 Name1 2014/10/19 09:05:21 F1
0000000004 Name2 2014/10/01 00:07:09 F2
The entry is not added. You'll have to modify the above to your business requirements

Calculating percentage of a groups from datatable

I have a datatable that looks like below
My result Should be A=40% , B=60% .. ie 2/5 and 3/5
Group name can be A, B, C, etc...
How can i calculate the figures based on that datatable values??
You could use LINQ and cast it to a dictionary:
DataTable dt = new DataTable("test1");
dt.Columns.AddRange(new DataColumn[] { new DataColumn("TASKID"), new DataColumn("GROUPID"), new DataColumn("GROUPNAME") });
dt.Rows.Add(new object[] { 12, 2, "A" });
dt.Rows.Add(new object[] { 13, 3, "B" });
dt.Rows.Add(new object[] { 12, 2, "A" });
dt.Rows.Add(new object[] { 14, 3, null });
dt.Rows.Add(new object[] { 15, 3, "B" });
var query = (from DataRow row in dt.Rows
group row by row["GROUPNAME"] into g
select g).ToDictionary(x => (x.Key.ToString() == "" ? "*" : x.Key.ToString()), x => (int)((x.Count() * 100) / dt.Rows.Count));
Iterate through the dictionary to display the values:
foreach(KeyValuePair<string,int> kvp in query)
Console.WriteLine(kvp.Key + " - " + kvp.Value.ToString());
The output:
A - 40
B - 40
* - 20
The percentage is cast as an int. simply change (int)((x.Count() * 100) / dt.Rows.Count) if you need more accurate values.
A simple way, through loop. The following should be similar to the one you require.
//Simulated datatable
DataTable table1 = new DataTable();
table1.Columns.Add(new DataColumn("TaskID", typeof(int)));
table1.Columns.Add(new DataColumn("GroupID", typeof(int)));
table1.Columns.Add(new DataColumn("GroupName", typeof(String)));
//Entered test values
DataRow dr1 = null;
dr1 = table1.NewRow();
dr1["TaskID"] = 12;
dr1["GroupID"] = 2;
dr1["GroupName"] = "A";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 13;
dr1["GroupID"] = 3;
dr1["GroupName"] = "B";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 14;
dr1["GroupID"] = 2;
dr1["GroupName"] = "A";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 15;
dr1["GroupID"] = 3;
dr1["GroupName"] = "B";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 16;
dr1["GroupID"] = 3;
dr1["GroupName"] = "B";
table1.Rows.Add(dr1);
//solution starts from here
Dictionary<string, int> totalCount = new Dictionary<string, int>();
for (int i = 0; i < table1.Rows.Count; i++)
{
if (totalCount.Keys.Contains(table1.Rows[i]["GroupName"].ToString()))
{
int currVal = totalCount[table1.Rows[i]["GroupName"].ToString()];
totalCount[table1.Rows[i]["GroupName"].ToString()] = currVal + 1;
}
else
{
totalCount[table1.Rows[i]["GroupName"].ToString()] = 1;
}
}
foreach (var item in totalCount)
{
MessageBox.Show(item.Value.ToString());
}
OR
//solution starts from here
var data = table1.AsEnumerable().GroupBy(m => m.Field<string>("GroupName")).Select(grp => new
{
GroupName = grp.Key,
Count = (int)grp.Count()
}).ToList();
Hope this helps
Use Linq
var data=datatable.AsEnumerable().GroupBy(m => m.Field<string>("GROUPNAME")).Select(grp => new
{
GROUPNAME= grp.Key,
Count = (int)grp.Count()
});

LINQ and C# summary

I have a relational table which has parent child rows. Some of the rows have a typeId and I would like to get a summary of the total cost by the type:
The list on the left is some sample data and the list on the right is the summary result expected.
The data is in a datatable. Can anyone help me with this.
ie:
typeId = 1: Which would be calculated by: 30 (total cost of 3,2) x 5 (qty of 2,1) x 3 (qty of 1,null)
(30 x 5 x 3) = 450
typeId = 2: Which would be calculated by: 12 (total cost of 5,1) x 3 (qty of 1,null)
x 15 (total cost 4,2) x 5 (qty of 2,1) x 3 (qty of 1,null)
(12 x 3) + (15 x 5 x 3) = 261
Here is some sample code
DataTable dt = new DataTable( "Summary" );
dt.Columns.Add( "Id", Type.GetType( "System.Int32" ) );
dt.Columns.Add( "ParentId", Type.GetType( "System.Int32" ) );
dt.Columns.Add( "Qty", Type.GetType( "System.Int32" ) );
dt.Columns.Add( "Cost", Type.GetType( "System.Decimal" ) );
dt.Columns.Add( "TotalCost", Type.GetType( "System.Decimal" ) );
dt.Columns.Add( "TypeId", Type.GetType( "System.Int32" ) );
dt.Rows.Clear();
DataRow row = dt.NewRow();
row["Id"] = 1;
row["ParentId"] = DBNull.Value;
row["Qty"] = 3;
row["Cost"] = 237.00;
row["TotalCost"] = 711.00;
row["TypeId"] = DBNull.Value;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 2;
row["ParentId"] = 1;
row["Qty"] = 5;
row["Cost"] = 45.00;
row["TotalCost"] = 225.00;
row["TypeId"] = DBNull.Value;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 3;
row["ParentId"] = 2;
row["Qty"] = 30;
row["Cost"] = 1.00;
row["TotalCost"] = 30.00;
row["TypeId"] = 1;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 4;
row["ParentId"] = 2;
row["Qty"] = 1;
row["Cost"] = 15.00;
row["TotalCost"] = 15.00;
row["TypeId"] = 2;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 5;
row["ParentId"] = 1;
row["Qty"] = 4;
row["Cost"] = 3.00;
row["TotalCost"] = 12.00;
row["TypeId"] = 2;
dt.Rows.Add( row );
If I understand you correctly, you need a recursive routine which is not something I would try in pure Linq, so I would try something like this.
var summary =
(from r in dt.AsEnumerable()
where r["TypeID"] != DBNull.Value
group r by (int) r["TypeId"] into results
select new
{
results.Key ,
TotalCost = results.Sum(r=> (decimal) r["TotalCost"] * GetParentsQty(r) )
}
);
public int GetParentsQty(DataRow child )
{
if (child["ParentID"] == DBNull.Value)
return 1;
var parent = (from row in dt.AsEnumerable()
where (int) child["ParentID"] == (int) row["Id"]
select row
).Single();
return (int) parent ["Qty"] * GetParentsQty(parent);
}

Categories