How can I write a linq query for this? - c#

I need to write following query in Linq to SQL but not sure what is the best way of doing, given it has two derived tables. Any suggestions.
SELECT A.ID
FROM
(
SELECT *
FROM Orders
WHERE ProductID = 5
) A
JOIN
(
SELECT CustomerID, MAX(Price) Price
FROM Orders
WHERE ProductID = 5
GROUP BY CustomerID
) B
ON A.CustomerID = B.CustomerID and A.Price = B.Price

var b = (
from o in db.Orders
where o.ProductID == 5
group o by o.CustomerID into og
select new {
CustomerID = og.Key
Price = Max(og.Price)
}
);
var a = (
from o in db.Orders
join p in b on new {a.CustomerID, a.Price} equals
new {b.CustomerID, b.Price}
where o.ProductID == 5
select a.ID
);
var r = a.ToString();
These two links are invaluable when forming things like this:
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
http://msdn.microsoft.com/en-us/vstudio/bb688085

Can you simplify this with LINQ, especially if you use method syntax instead of query syntax.
orders.Where(o => o.ProductID == 5)
.GroupBy(o => o.CustomerID)
.SelectMany(g => g.Where(o => o.Price == g.Max(m => m.Price)));
My advice when writing LINQ, do not simply attempt to convert a SQL statement exactly. Think about the desired result and develop a solution designed for LINQ.

Something along these lines:
var result = from a in context.Orders
join b in (context.Orders.Where(o => o.ProductID == 5).GroupBy(o => o.CustomerID).Select(g => new { CustomerID = g.Key, Price = g.Max(o => o.Price)))
on new {a.CustomerID, a.Price} equals new {b.CustomerID, b.Price}
where a.ProductID == 5
select a.ID;

Related

What is the linq equivalent for the TSQL with join and group by?

I'm trying to create a linq query from the following sql.
Couldn't find an example that had a join in it with multiple tables and a group by that had different fields from different tables.
select t2.field1, t1.field1, count(t2.field1) from Table1 t1
join Table2 t2 on t1.pkfield = t2.pkfield
group by t2.field1, t1.field1
having count(*) > 1
order by t2.pkfield1
Tried the following:
var test =
from t1 in db.Table1
join t2 in db.Table2 on t1.pkfield equals t2.pkfield
group x by (t1.field1, t2.field1)
select t1.field1, t2.field2
You do not post entity classes and input examples so I can't verify my code. But I hope that concept is clear
db.Table1.SelectMany(t1 => db.Table2.Select(t2 => new { t2.field1, t1.field1, t2.pkfield1 }))
.GroupBy(x => new { t1Field1 = t2.field1, t2Field1 = t1.field1 })
.Where(g => g.Count() > 1)
.OrderBy(g => g.Min(x => x.pkfield1))
.Select(g => new { g.Key.t1Field1, g.Key.t2Field1, g.Count() });
In code above db is your custom DbContext and SelectMany will be translated into inner join
I don't know how to do it in a single query, but I have accomplished something similar as follows. Hope it helps you or at least points you in the right direction.
var result = from p in person
join o in orders on p.OrderId equals o.OrderId
// build your custom object with columns from multiple tables.
select new { o.OrderId, p.OrderId};
// now group the result
var grouped = result.GroupBy(x => x.OrderId)
.Where(y=> y.Count() > 1);

How to call a method within .Select in Linq query without having to write select twice?

The following is my code that works. But as you can see I am having to write select twice
var lstCargoRequestVM =
(from c in db.Cargo
join v in db.Vehicles on c.VehicleID equals v.VehicleID
join cmp in db.Companies on c.CompanyID equals cmp.CompanyID
where c.Isdeleted == false && c.IsActive == true
select new CargoRequestVM
{
CargoId = c.CargoID,
CompanyName = cmp.CompanyName,
VehicleNo = v.VehicleNo,
Date = c.DateOfPassage,
Type = c.Type.ToString()
})
.AsEnumerable()
.Select(x => new CargoRequestVM
{
CargoId = x.CargoId,
CompanyName = x.CompanyName,
VehicleNo = x.VehicleNo,
Date = x.Date,
Type = CargoElements.CargoTypeName(x.Type.ToString())
}).ToList();
Is it possible to do the same without having to write select twice? There could be more than a dozen properties in certain case. I don't want to make my code unnecessarily lengthy.
Probably that wouldn't have a translation to underlying database and thus you need to write basically twice. However you can apply the AsEnumerable() after where using method syntax like (assuming you in fact have a good relational schema defined and navigational properties set - in Linq you very rarely need join keyword):
stVM = db.Cargo
.Include( c => c.Vehicle )
.Include( c => c.Company )
.Where( c => !c.Isdeleted && c.IsActive )
.AsEnumerable()
.Select( c => new CargoRequestVM
{
CargoId = c.CargoID,
CompanyName = c.Company.CompanyName,
VehicleNo = c.Vehicle.VehicleNo,
Date = c.DateOfPassage,
Type = CargoElements.CargoTypeName(c.Type.ToString())
}).ToList();

How to do this query using expression in LINQ?

select t.*
from Task t
inner join Project p on a.ProjectId = t.ProjectId
where p.ProjectTypeId IN ( select ptg.ProjectTypeId
from UserGroup ug
inner join ProjectTypeGroup ptg on ug.GroupId = ptg.GroupId
where ug.UserId = 1 -- MUTABLE VALUE
)
In the attempt query given by you, I don't see Task being used.
Since there is no relation between your sub-query and your main query, you should split them in to two queries:
var projTypeIds = (from ug in UserGroup
join ptg in ProjectTypGroup on ug.GroupId equals ptg.GroupId
where ug.UserId == 1
select ptg.ProjectTypeId).ToList()
Once you have your output, check for .Contains
var task = (from t in Task
join p in Project on p.ProjectId equals t.ProjectId
where projTypeIds.Contains(p.ProjectTypeId)
select t).FirstOrDefault();
I assume you need only one object from this query hence used .FirstOrDefault(), if you are expecting a list using .ToList()
You can use this.
from t in Task
join p in Project
on t.ProjectId equals p.ProjectId
let subQ = ( from ug in UserGroup
join ptg in ProjectTypeGroup
on ug.GroupId equals ptg.GroupId
where ug.UserId == 1
select ptg.ProjectTypeId)
where subQ.Contains(p.ProjectTypeId)
select t
or you can use this.
int loggedUserId = 1;
var _userGroups =
UserGroup.Join(ProjectTypeGroup,
t => t.GroupId, p => p.GroupId,
(t, p) => new {t, p})
.Where(n => n.t.UserId == loggedUserId)
.Select(s => s.p.ProjectTypeId);
var projectTypeIds =
Task.Join(Project,
t => t.ProjectId, p => p.ProjectId,
(t, p) => new {t, p})
.Where(n => _userGroups.Contains(n.p.ProjectTypeId) )
.Select(n => n.t);
projectTypeIds.ToList();

Sql Query into Linq to Entity Framework

I'm wondering if it is even possible to write the below sql query as a LINQ to Entity statement. Below is a simplified example of a real world problem that I'm trying to figure out:
Select
c.CustomerID,
c.CustomerName,
(Select count(p.ProductID) from Products p
where p.CustomerID = c.CustomerID and p.Category = 'HomeAppliance') as ApplianceCount,
(Select count(p.ProductID) from Products p
where p.CustomerID = c.CustomerID and p.Category = 'Furnishing') as FurnishingCount
from Customer c
where
c.CustomerMarket = 'GB'
order by c.CustomerID desc;
Any suggestions would be appreciated. Performance of the LINQ to Entity would need to be considered as it would involve retrieving lot of rows.
Something like (assuming the obvious context):
var res = await (from c in dbCtx.Customers
where c.CustomerMarket = "GB"
let homeCount = c.Products.Where(p => p.Category = "HomeAppliance").Count()
let furnCount = c.Products.Where(p => p.Category = "Furnishing").Count()
orderby c.CustomerID descending
select new {
CustomerID = c.CustomerID,
CustomerName = c.CustomerName,
ApplianceCount = homeCount,
FurnishingCount = furnCount
}).ToListAsync();
Performance of the LINQ to Entity would need to be considered as it would involve retrieving lot of rows.
You'll need to confirm the SQL generated is reasonable (best way to help that is not getting more columns than you need), after that performance is down to how well the server runs that SQL.
Yes, it is possible:
customers
.Where(cust => cust.CustomerMarket == "GB")
.Select(cust => new
{
cust.CustomerId,
cust.CustomerName,
ApplianceCount = products
.Where(prod => prod.CustomerId == cust.CustomerId && prod.Category == "HomeAppliance")
.Select(prod => prod.ProductId)
.Count(),
FurnishingCount = products
.Where(prod => prod.CustomerId == cust.CustomerId && prod.Category == "Furnishing")
.Select(prod => prod.ProductId)
.Count(),
});
Here both customers and products are IQueryable<T>s of the respective type.

modify linq to get first 5 elements

var lastArticles = from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name};
I need to get the first 5 elements.
I'm doing it by
.Take(5)
but is there a way to do in in the linq statement?
No, you need to use Skip() and Take() as method calls. There is no LINQ-specific equivalent.
var lastArticles = (from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name }).Take(5);
A linq Query should always be separate from the products of running that query.
.Take() produces results, and thus should be separate and distinct from the query.
//data query
var lastArticlesQuery = from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name};
//results of that query at this time
var lastArticles = lastArticlesQuery.Take(5);
This code is only syntatic sugar, utlimately it will be converted to a LINQ-methods chain that will look something like:
var lastArticles = be.MyTable
.Where(a => a.id == 1)
.Join(be.OtherTable, a => a.parent, c => c.id,
(a, c) => new { a, c})
.OrderByDescending(#t => #t.a.timestamp)
.Select(#t => new { #t.a, cName = #t.c.name });
So having a keyword for Take() would only add to the sytactic sugar and it would need to be re-converted as well.
In short, no, the only way is to use the Take() method.

Categories

Resources