c# LINQ DataTable substraction - linq

i have two DataTables which has different columns but one column is same.
i want to get new DataTable which contains row from dt1 which are not exist in dt2.
how can i do it using LINQ?
that's is my current code, but it work only for Datatables with same columns:
private void Delete_HPM_Deleted_Points(int YellowCard_Request_ID)
{
DataTable dt_hpm_points_DB = DataAccessLayer.Get_YellowCard_Request_HPM_Points(YellowCard_Request_ID);
//dt_hpm_points_DB.Columns.Remove("HPM_Tool_ID");
//dt_hpm_points_DB.Columns.Remove("HPM_Point_Message");
//dt_hpm_points_DB.Columns.Remove("HPM_Point_isDisabled");
//dt_hpm_points_DB.Columns.Remove("HPM_Point_Comments");
DataTable dt_hpm_points_Local = UserSession.Get_User_YC_HPM_Added_Points_DATATABLE();
//dt_hpm_points_Local.Columns.Remove("HPM_Tool_ID");
//dt_hpm_points_Local.Columns.Remove("HPM_Point_Message");
//var rowsInFirstNotInSecond = dt_hpm_points_DB.Rows.Where(r1 => !dt_hpm_points_Local.Rows.Any(r2 => r1.ID == r2.ID));
DataTable dt_points_to_remove = dt_hpm_points_DB.AsEnumerable().Except(dt_hpm_points_Local.AsEnumerable(), DataRowComparer.Default).CopyToDataTable();
foreach (DataRow dr in dt_points_to_remove.Rows)
{
DataAccessLayer.Delete_YellowCard_Request_HPM_Point(YellowCard_Request_ID, dr["HPM_Point_ID"].ToString());
}
}

You can use Linq-To-DataSet with a "Left Outer Join":
var justInDT1 = from r1 in dt1.AsEnumerable()
join r2 in dt2.AsEnumerable()
on r1.Field<int>("PK_ID") equals r2.Field<int>("FK_ID") into GJ
from subRow in GJ.DefaultIfEmpty()
where subRow == null
select r1;
How to: Perform Left Outer Joins (C# Programming Guide)

Related

Linq to SQL conversion...unable to add second COUNT

I'm trying to convert my SQL statement to a Linq statement and I'm not sure how to add the second COUNT to it. This is my SQL statement
SELECT l.Campus_Name, Labs = COUNT(*), LabsWithSubnets = COUNT(s.Lab_Space_Id)
FROM vw_Lab_Space l
LEFT JOIN vw_Subnet s on l.Lab_Space_Id = s.Lab_Space_Id
GROUP BY l.Campus_Name
ORDER BY 1
and this is my LINQ statement so far:
from l in Vw_Lab_Space
from s in Vw_Subnet
.Where(s => s.Lab_Space_Id == l.Lab_Space_Id)
.DefaultIfEmpty() // <=- triggers the LEFT JOIN
group l by new { l.Campus_Name } into g
orderby g.Key.Campus_Name
select new {
Campus_Name = g.Key.Campus_Name,
Labs = g.Count()
}
So I have everything but the LabsWithSubnets part in there. I'm just not sure how to add that in as I can't just do an s.Lab_Space_id.Count() in the select statement.
If you need table structure and sample data please see Need help creating an OUTER JOIN to count spaces.
Using your query as a basis, you need the groups to include s so you can count when non-null (I also removed the unnecessary anonymous object around the grouping key):
from l in Vw_Lab_Space
from s in Vw_Subnet
.Where(s => s.Lab_Space_Id == l.Lab_Space_Id)
.DefaultIfEmpty() // <=- triggers the LEFT JOIN
group new { l, s } by l.Campus_Name into g
orderby g.Key
select new {
Campus_Name = g.Key,
Labs = g.Count(),
LabsWithSubnets = g.Count(ls => ls.s != null)
}
However, rather than translate the SQL, I would probably take advantage of LINQ's group join to handle the query slightly differently:
var ans = from l in Vw_Lab_Space
join s in Vw_Subnet on l.Lab_Space_Id equals s.Lab_Space_Id into sj
group new { l, sj } by ls.Campus_Name into lsjg
select new {
Campus_Name = lsjg.Key,
NumLabs = lsjg.Count(),
LabsWithSubnets = lsjg.Sum(lsj => lsj.sj.Count())
};
PS Even in your query, I would use join...from...DefaultIfEmpty rather than from...from...where but depending on your database engine, may not matter.

Dynamically adding the columns in group by clause of linq

I am getting a datatable from service. I am using linq to calculate of sum new columns. I want all the coumns in datatable originally plus the new columns calculated in linq.
Problem is columns in datatable are not fixed. How would I dynamically add the columns in select clause of linq.
Below is code snippet:
DataTable dt = ds.Tables[0];
var orderCtr =
from o in dt.AsEnumerable()
where o.Field<string>(Constants.GENDER_NAME) != "Unknown"
group o by new
{
odr_id = o.Field<int>(Constants.ORDER_ID),
//NEED TO ADD COLUMNS DYNAMICALLY HERE. MEANS IF THEY ARE IN DATATABLE.
}
into g
select new
{
//NEED TO ADD COLUMNS DYNAMICALLY HERE. MEANS IF THEY ARE IN DATATABLE.
odr_id = g.Key.odr_id,
ac_gr_imp = g.Sum(r => r.Field<long>(Constants.GENDER_IMPRESSION)),
ac_gr_clk = g.Sum(r => r.Field<long>(Constants.GENDER_CLICK)),
Ctr = (double)g.Sum(r => r.Field<long>(Constants.GENDER_IMPRESSION)) / g.Sum(r => r.Field<long>(Constants.GENDER_CLICK)),
};
You might be interested in the Dynamic LINQ library. Dynamic LINQ allows you to specify queries or parts of queries by building strings.
Scott Guthrie described it back in 2008 (http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx) and there is also a NuGet package of it in case you use .NET 4 (http://www.nuget.org/packages/System.Linq.Dynamic).

LINQ Where condition against datatable

I have a DataTable. I would like to filter DataRows where City = "Hongkong".
How to apply LINQ against DataRow?
You could use the following Query
var filter = testTable.AsEnumerable().
Where(x => x.Field<string>("City") == "HongKong");
var result = dr.Where(r => r.Field<string>("City") == "Hongkong");
Using LINQ to DataSets, you can do the following:
DataTable table;
var rows =
from row in table.AsEnumerable()
where row.Field<string>("City") == "Hongkong"
select row;

Link OrderBy method not taking effect after Union method

I'm using LINQ's Union method to combine two or more collections. After that I'm trying to apply sorting to the combined collection by calling OrderBy on a field that is common to the collections. Here is how I am applying sorting:
combinedCollection.OrderBy(row => row["common_field"]);
combinedCollection is defined as:
Enumerable<DataRow> combinedCollection;
I need the sorting to be applied to the entire combined collection. For some reason, that is not happening. Instead I see there is sorting applied on some other field separately within each 'collection' block within the combined collection
And idea why??
First Edit
foreach (....)
{
if (combinedCollection != null)
{
combinedCollection = combinedCollection.Union(aCollection);
}
else
{
combinedCollection = aCollection;
}
}
Second Edit
_Cmd.CommandText = "SELECT Person.Contact.FirstName, Person.Contact.LastName, Person.Address.City, DATEDIFF(YY, HumanResources.Employee.BirthDate, GETDATE()) AS Age"
+ " FROM HumanResources.EmployeeAddress INNER JOIN"
+ " HumanResources.Employee ON HumanResources.EmployeeAddress.EmployeeID = HumanResources.Employee.EmployeeID INNER JOIN"
+ " Person.Address ON HumanResources.EmployeeAddress.AddressID = Person.Address.AddressID INNER JOIN"
+ " Person.Contact ON HumanResources.Employee.ContactID = Person.Contact.ContactID AND HumanResources.Employee.ContactID = Person.Contact.ContactID AND "
+ " HumanResources.Employee.ContactID = Person.Contact.ContactID AND HumanResources.Employee.ContactID = Person.Contact.ContactID";
DataTable employeeTable = new DataTable();
_Adpt.Fill(employeeTable);
DataRow[] allRows = null;
allRows = employeeTable.Select("");
IEnumerable<DataRow> filteredEmployeeRows;
filteredEmployeeRows = from row in allRows select row;
// Declare a variable to hold the city-filtered rows and set it to null for now
IEnumerable<DataRow> cityFilteredEmployeeRows = null;
//Copy filtered rows into a data table
DataTable filteredEmployeeTable = filteredEmployeeRows.CopyToDataTable<DataRow>();
foreach (DataRowView city in CityListBox.SelectedItems)
{
// create an exact copy of the data table
DataTable filteredEmployeeCopyTable = filteredEmployeeTable.Copy();
// Enumerate it
IEnumerable<DataRow> filteredEmployeeRowsInSingleCity = filteredEmployeeCopyTable.AsEnumerable();
// Apply the city filter
filteredEmployeeRowsInSingleCity = _ApplyCityFilter(filteredEmployeeRowsInSingleCity, city["City"].ToString());
if (cityFilteredEmployeeRows != null)
{
// Combine the filtered rows for this city with the overall collection of rows
cityFilteredEmployeeRows = cityFilteredEmployeeRows.Union(filteredEmployeeRowsInSingleCity);
}
else
{
cityFilteredEmployeeRows = filteredEmployeeRowsInSingleCity;
}
}
//apply ordering
cityFilteredEmployeeRows.OrderBy(row => row["Age"]);
//cityFilteredEmployeeRows.OrderByDescending(row => row["Age"]);
EmployeeGridView.DataSource = cityFilteredEmployeeRows.CopyToDataTable<DataRow>();
.......
private IEnumerable<DataRow> _ApplyCityFilter(IEnumerable<DataRow> filteredEmployeeRows, string city)
{
IEnumerable<DataRow> temp = filteredEmployeeRows;
filteredEmployeeRows = from row in temp
where row["City"].ToString() == city
select row;
return filteredEmployeeRows;
}
I think you have a problem with the LINQ lazy evaluation, I would have to investigate to find out wich part causes the problem.
Using the foreach(var item...) in lazy functions has already bitten me (because when executed later they all reference the last iterated item), but in your case it doesn't look like this is the problem.
To check it is the really the issue you can just use a DataRow[] in place of the IEnumerable<DataRow> and call .ToArray() after every LINQ function.
Edit: I'm not sure I got your code right but can't you just use:
var cities = CityListBox.SelectedItems.Cast<DataRowView>()
.Select(city => city["City"].ToString())
.ToArray();
var rows = allRows
.Where(r => cities.Contains((string)r["City"]))
.OrderBy(r => (int?)r["Age"])
.ToArray(); // if you want to evaluate directly, not mandatory

Linq expression for left join and filter for the inner table

I want to know how to make Linq expression that has the same effect as these SQL query
SELECT item.*, priceforitem.*
FROM
item
LEFT JOIN priceforitem
ON priceforitem.ItemID = item.ItemID
AND priceforitem.PriceID = ?PriceID
I already make it using the Method query but I don't know if it will produce the same result
db.Items
.GroupJoin(
db.PriceForItems.Where(pi => pi.PriceID == id),
i => i.ItemID,
pi => pi.ItemID,
(i, pi) => new { Item = b, Prices = pi })
.SelectMany(
a => a.Prices.DefaultIfEmpty(),
(i, pi) => new
{
ItemID = i.Item.ItemID,
Code = i.Item.Code,
Name = i.Item.Name,
PriceForItemID = pi.PriceForItemID,
Price = pi.Price
})
and then after thinking for awhile i shorten it like this
db.Items
.SelectMany(
i => db.PriceForItems.Where(
pi => pi.PriceID == id
&& pi.ItemID = i.ItemID).DefaultIfEmpty(),
(i, pi) => new
{
ItemID = i.Item.ItemID,
Code = i.Item.Code,
Name = i.Item.Name,
PriceForItemID = pi.PriceForItemID,
Price = pi.Price
})
I am new to Linq, and I don't know which is better and how to convert it to Linq query statement.
First of all your sql query. It is effectively and inner join because the where condition will filter out all rows where data from priceforitem is null. If you do want to convert same query to linq you can do it like
from i in db.Items
join p in db.PriceforItems on
i.ItemId equals p.ItemId into tempvals
from t in tempvals.DefaultIfEmpty()
where t.PriceId == id
select new{i.ItemId, ..., t.PriceId, t...., t....}
I mostly write linq queries instead of expressions where they are more readable to me. If you still want to get an expression, you can write a valid linq query and paste it into Linqpad and it will give the result as well as lambda expression of your query.

Resources