Entity Framework Left Join on aggregate query - c#

I have an EF query that contains a number of aggregate sub queries (Count, Sum and Max). There are two problems.
I need the joins on the sub queries to be left joins so that records are returned even if there are no results in the aggregate sub queries. At present, records are only returned if all sub queries return records.
The resulting list of WholesaleCustomerAndAggregateOrders objects that are returned contain a Contact object that needs to also include Addresses and Counties. I added Include(c => c.Addresses.Select(a => a.Country)) to the query but the Contact objects don't contain any Address objects.
Any assistance with either issue would be appreciated. Full query below.
var month1Date = DateTime.Today.AddMonths(-1);
var month3Date = DateTime.Today.AddMonths(-3);
var month6Date = DateTime.Today.AddMonths(-6);
var month12Date = DateTime.Today.AddMonths(-12);
var db = GetNewContext();
var qry = from c in db.Contacts
.Include(c => c.Addresses.Select(a => a.Country))
join orderCount in
(
from o in db.WholesaleOrders
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalOrders = g.Count()
}
) on c.Id equals orderCount.ContactId
join month1Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month1Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month1Value.ContactId
join month3Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month3Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month3Value.ContactId
join month6Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month6Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month6Value.ContactId
join month12Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month12Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month12Value.ContactId
join month12Quantity in
(
from o in db.WholesaleOrders
where o.OrderDate >= month12Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
OrderCount = g.Count()
}
) on c.Id equals month12Quantity.ContactId
join lastOrderDate in
(
from o in db.WholesaleOrders
group o by o.ContactId into g
select new
{
ContactId = g.Key,
LastOrderDate = g.Max(r => r.OrderDate)
}
) on c.Id equals lastOrderDate.ContactId
select new WholesaleCustomerAndAggregateOrders
{
Contact = c,
TotalOrders = orderCount.TotalOrders,
Month1Value = month1Value.TotalValue,
Month3Value = month3Value.TotalValue,
Month6Value = month6Value.TotalValue,
Month12Value = month12Value.TotalValue,
Month12OrderCount = month12Quantity.OrderCount,
LastOrderDate = lastOrderDate.LastOrderDate
};
return await qry.ToListAsync();

How about this:
db.WholesaleOrders
.GroupBy(o => o.ContactId)
.Select(a => new {
a.Key,
TotalOrders = a.Count(),
LastOrderDate = a.Max(r => r.OrderDate),
Month1Value = a.Where(b => b.OrderDate >= month1Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price),
Month3Value = a.Where(b => b.OrderDate >= month3Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price),
Month6Value = a.Where(b => b.OrderDate >= month6Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price),
Month12Value = a.Where(b => b.OrderDate >= month12Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price)
}).ToListAsync();
UPDATE: Add another property to projection:
Addresses = db.Addresses.Where(ad => ad.ContactId == a.Key);
where db is your context, and ad.ContactId is FK of the contact in Address table.
For this to work, multipleactiveresultsets property of the connection must be set to True.

Related

How to Convert SQL query to LINQ (to get last record each bookingStatus)

Need some help translating below query to LINQ.
;With BookingWithLastStatus
as
(
Select *, Rnk = ROW_NUMBER() over (partition by BookingId order by Id desc)
from BookingStatus
)
Select *
from BookingWithLastStatus
where Rnk=1 AND StatusId = 3
I've done LINQ below but it is not getting the correct records.
var BookStatus = from p in _context.Set<BookingStatus>()
where p.StatusId == 3
group p by p.BookingId into opt
select new {
BookingId = opt.Key,
Id = opt.Max(x => x.Id)
};
The SQL query is getting 1 record only which is correct and my LINQ is getting multiple records.
UPDATE:
I did like this:
Get all the BookingStatus first
var GetAllBookStatus = await _context.Set<BookingStatus>()
.ToListAsync();
Then do the filtering based from the SQL Query I need.
var FilteredBookStatus = GetAllBookStatus
.OrderByDescending( x => x.Id )
.GroupBy(person => person.BookingId)
.Select( group => new { Group = group, Count = group.Count() } )
.SelectMany( groupWithCount =>
groupWithCount.Group.Select( b => b)
.Zip(
Enumerable.Range( 1, groupWithCount.Count ),
( b, i ) => new {
b.Id,
b.BookingId,
b.BookingMWABId,
b.BookStatus,
b.CreatedBy,
b.CreatedDate,
b.Destination,
b.InternalStatus,
b.LineNum,
b.ModifiedBy,
b.ModifiedDate,
b.Module,
b.ReasonCode,
b.ReceivedBy,
b.RefNo,
b.StatusId,
b.TimeStamp,
RowNumber = i }
)
)
.Where(a => a.StatusId == 3 && a.RowNumber == 1)
.ToList();
But I'm not so confident on Getting all records, as it will grow some time. Is there anything I can change from my code?
With EF core 6.x, you can do the following. It is not optimal case as in your SQL, but should work:
var BookStatus =
from p in _context.Set<BookingStatus>()
group p by p.BookingId into g
select g.OrderByDescending(x => x.Id).First();
BookStatus = BookStatus.Where(p => p.StatusId == 3);
Or another variant
var BookStatus = _context.Set<BookingStatus>().AsQueryable();
BookStatus =
from d in BookStatus.Select(d => new { d.BookingId }).Distinct()
from p in BookStatus
.Where(p => p.BookingId == d.BookingId)
.OrderByDescending(p => p.Id)
.Take(1)
select p;
BookStatus = BookStatus.Where(p => p.StatusId == 3);

Entity Framework equivalent of the following query?

Can someone tell me how to write an EF query that equals to this:
SELECT OI.orderid,
OI.inventoryid,
VP.vendorid
FROM orders O
INNER JOIN orderitems OI
ON O.orderid = OI.orderid
INNER JOIN vendorparts VP
ON VP.inventoryid = OI.inventoryid
WHERE (SELECT Count(*)
FROM vendorparts
INNER JOIN vendors
ON vendorparts.vendorid = vendors.vendorid
AND vendors.candropship = 1
WHERE vendorparts.inventoryid = VP.inventoryid
AND vendorparts.vendorid IN ( 1, 17 )) > 1
I'm trying to do something like this...
var q = from o in _context.Orders
join oi in _context.Orderitems on o.Orderid equals oi.Orderid
join vp in _context.Vendorparts on oi.Inventoryid equals vp.Inventoryid
where (from vp2 in _context.Vendorparts
where vp2.Inventoryid == vp.Inventoryid
select count(*)) > 1
but obviously that is syntatctically not allowed.
Long answer:
var vendorIds = new int[] { 1, 17 };
_context
.Orders
.Join(_context.Orderitems,
o => o.Orderid,
oi => oi.Orderid,
(o, oi) => new { o, oi })
.Join(_context.Vendorparts,
o_oi => o_oi.oi.Inventoryid,
vp => vp.Inventoryid,
(o_oi, vp) => new { o_oi.o, o_oi.oi, vp })
.Where(o_oi_vp => _context.Vendorparts
.Where(vp
=> vp.Inventoryid == o_oi_vp.vp.Inventoryid
&& vendorIds.Contains(vp.Vendorid))
.Join(_context.Vendors.Where(v => v.Candropship),
vp => vp.Vendorid,
v => v.Vendorid,
(vp, v) => new { vp, v })
.Any())
.Select(o_oi_vp => new {
o_oi_vp.oi.orderid,
o_oi_vp.oi.inventoryid,
o_oi_vp.vp.vendorid
});
Short answer: use a stored procedure.

Group By Query with Entity Framework Query Method

i have this query and i wanna to change to linq query methods:
select o.OrderID,o.OrderNo, o.OrderDate, SUM(TotalAmount) Total
from orders o inner join
OrderDetails e on o.OrderID=e.OrderID
group by o.OrderNo, o.OrderDate,o.OrderID order by o.OrderNo desc
i have been trying the follow:
public List<Orders>List()
{
var list = new List<Orders>();
try
{
using (var db = new MyDatabaseEntities())
{
list = db.Orders.Select(o => new { o.OrderID, o.OrderNo, o.OrderDate, o.OrderDetails.TotalAmount}).
GroupBy(x => new { x.OrderID, x.OrderNo, x.OrderDate }).
Select(o => new
{
o.Key,
id = o.OrderID,
order = o.NumOrder,
date = o.OrderDate,
Total = o.Sum(o.TotalAmount)
}).Tolist();
}
}
catch(Exception e)
{
throw new Exception(e.Message);
}
return list;
}
Someone can Help me?
I think try this:
var list=db.Orders.Join(OrderDetails,
o=>o.OrderID
e=>e.OrderID
(o,e)=>new{Orders=o,OrderDetails=e})
.GroupBy(o=>new {o.OrderNo, o.OrderDate,o.OrderID})
.OrderBy(x=>x.Key.OrderNo)
.Select(o=>new{
o.Key.OrderID,
o.Key.OrderNo,
o.Key.OrderDate,
Total=o.SUM(x=>x.TotalAmount)})
.ToList();
Try this :
Sql like syntax
from t in TblOrders
join d in TblOrderDetails
on t.Id equals d.OrderId
group d by new {t.Id, t.OrderNo, t.OrderDate} into g
orderby g.Key.OrderNo descending
select new
{
ID = g.Key.Id,
OrderNo = g.Key.OrderNo,
OrderDate = g.Key.OrderDate,
amount = g.Sum(x=>x.Rate)
}
lambda like:
TblOrders
.Join (
TblOrderDetails,
t => t.Id,
d => d.OrderId,
(t, d) =>
new
{
t = t,
d = d
}
)
.GroupBy (
temp0 =>
new
{
Id = temp0.t.Id,
OrderNo = temp0.t.OrderNo,
OrderDate = temp0.t.OrderDate
},
temp0 => temp0.d
)
.OrderByDescending (g => g.Key.OrderNo)
.Select (
g =>
new
{
ID = g.Key.Id,
OrderNo = g.Key.OrderNo,
OrderDate = g.Key.OrderDate,
amount = g.Sum (x => x.Rate)
}
)

How to write lambda expression for an sql expression?

I have an SQL expression
select S.SpecialtyName, COUNT(distinct SUC.SiteUserId) as Subscribers
from SiteUserContent SUC Inner join
Specialties S on SUC.SpecialtyId = S.SpecialtyId Inner join
SiteUser SU on SUC.SiteUserId = SU.SiteUserId
where SU.DeletedFlag = 0
group by S.SpecialtyName
Order by S.SpecialtyName
What will be the corresponding LINQ expression for the same?
from suc in context.SiteUserContent
join s in context.Specialties on suc.SpecialtyId equals s.SpecialtyId
join su in context.SiteUser on suc.SiteUserId equals su.SiteUserId
where su.DeletedFlag == 0
select new { suc.SiteUserId, s.SpecialityName } into x
group x by x.SpecialityName into g
orderby g.Key
select new {
SpecialityName = g.Key,
Subscribers = g.Select(i => i.SiteUserId).Distinct().Count()
}
Generated SQL will not be same, but I think result of query execution should be same.
var results = contex.SiteUserContent
.Join(context.Specialties, suc => suc.SpecialtyId, s => s.SpecialtyId, (suc, s) => new { suc, s })
.Join(context.SiteUser, i = i.suc.SiteUserId, su => su.SiteUserId, (i, su) => new { suc = i.suc, s = i.s, su = su })
.Where(i => i.su.DeletedFlag == 0)
.GroupBy(i => i.s.SpecialtyName)
.Select(g => new {
SpecialityName = g.Key,
Subscribers = g.Select(i => i.suc.SiteUserId)
.Distinct()
.Count()
})
.OrderBy(i => i.SpecialityName);

what am i doing wrong in constructing Linq query

I am trying to write equivalent linq code for following query.
SELECT A.*
FROM
(
SELECT * FROM TableA
WHERE id = 100
) a
JOIN
(
SELECT Name, MAX(AnotherId) AnotherId
FROM TableA
WHERE id = 100
GROUP BY Name
) b
on a.Name = b.Name and a.AnotherId = b.AnotherId
This is the linq
var Collection = from R in DbContext.TableA
join G in (DbContext.TableA.Where(r => r.Id == 100).GroupBy(r => new { r.Name, r.AnotherId } ).Select(g => new { Name = g.Name , AnotherId = g.Max(o => o.AnotherId) }))
on new { R.Name, R.AnotherId } equals new { G.Name, G.AnotherId }
where R.Id == 100
select R;
But I am getting following compilation error that I don’t know how to fix. Any thoughts
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
Error 7 'System.Linq.IGrouping' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)
You group by r.Name, r.AnotherId, when you just want to group by r.Name.
var Collection = from R in DbContext.TableA
join G in (DbContext.TableA
.Where(r => r.Id == 100)
.GroupBy(r => r.Name)
.Select(g => new { Name = g.Key , AnotherId = g.Max(o => o.AnotherId) }))
on new { R.Name, R.AnotherId } equals new { G.Name, G.AnotherId }
where R.Id == 100
select R;
And to have all in Fluent Syntax
var collection = DbContext.TableA
.Where(t1 => t1.Id == 100)
.Join(DbContext.TableA
.Where(t2 => t2.Id == 100)
.GroupBy(t2 => t2.Name)
.Select(group => new{Name = group.Key,
AnotherId = group.Max(e => e.AnotherId)})
),
t1 => new{t1.Name, t1.AnotherId} ,
t2 => new{t2.Name, t2.AnotherId},
(t1, t2) => t1);
llHi you need the following syntax, notice the addition of 'Key'
var Collection = from R in DbContext.TableA
join G in (DbContext.TableA.Where(r => r.Id == 100)
.GroupBy(r => new { r.Name, r.AnotherId } )
.Select(g => new { Name = g.Key.Name , AnotherId = g.Max(o => o.AnotherId) }))
on new { R.Name, R.AnotherId } equals new { G.Name, G.AnotherId }
where R.Id == 100
select R;
I would recommend using the query syntax for all parts of your query. By doing it this way, your linq query will have a more similar structure to your original sql query. It would look like this:
var query =
from a in
(from x in DbContext.TableA
where x.ID == 100
select x)
join b in
(from x in DbContext.TableA
where x.ID == 100
group x by x.Name into x
select new
{
Name = x.Key,
AnotherId = x.Max(o => o.AnotherId),
})
on new { a.Name, a.AnotherId } equals new { b.Name, b.AnotherId }
select a;
saj and Raphael both have found good points:
.GroupBy(r => new { r.Name, r.AnotherId } )
.Select(g => new { Name = g.Name , AnotherId = g.Max(o => o.AnotherId) }))
Groups don't have a Name. Each group has a Key (and the Key has a Name and AnotherId).
Since you want the Max(AnotherId), you don't want to include AnotherId in your grouping Key (same as it is not present in the GroupBy clause of the original query).
.GroupBy(r => r.Name) //the Name is the Key
.Select(g => new { Name = g.Key, AnotherId = g.Max(o => o.AnotherId) }))

Categories