LINQ query to calculate values from joined collection - c#

I'm new to using LINQ to join collections and peforming calculations. I have the following query which joins a collection of RailwayStation objects and ExpenditureData objects joined by StationId.
var joinedData = (from s in stations join e in expenditureData on s.stationId
equals e.StationId select s).Distinct();
What LINQ do I need to get the top 10 stations which the highest expenditureAmount (which is a property in expenditureData) from the joinedData collection I have created?

I'm trying to group the data by StationCargoCode (a property in
Station object) to get the top 10 StationCargoCodes with the highest
expenditure data and get the total expenditure data too.
You could try the following query:
var joinedData = (from s in stations
join e in expenditureData
on s.stationId equals e.StationId
group by s.StationCargoCode into gr
select new
{
StationCargoCode = gr.Key,
TotalExpenditureAmount = gr.Sum(ed=>ed.expenditureAmount)
}).OrderByDescending(sc=>sc.TotalExpenditureAmountExpenditureAmount)
.Take(10);
Initially we join our data based on the station Id.
Then we group by the joined results based on the StationCargoCode.
After the grouping, we project each group to an anonymous object with two values, one the key used for the grouping and the other the sum of the expenditureAmount for that key.
Last, we order by our results in descending order based on the TotalExpenditureAmount and we pick up the first 10.

You can try this:
var joinedData = (from s in stations
join e in expenditureData on s.stationId equals e.StationId into g
select new{s,g})
.OrderByDescending(v=>v.g.Max(r=>r.expenditureAmount))
.Take(10)
.Select(v=>v.s);
Update
var joinedData = (from s in stations
join e in expenditureData on s.stationId equals e.StationId into g
select new{s.StationCargoCodes ,Max=g.Max(r=>r.expenditureAmount)})
.OrderByDescending(v=>v.Max)
.Take(10);
If you want just the StationCargoCodes then add a Select call at the end like this:
.Select(e=>e.StationCargoCodes);

Related

Entity Framework Core: join two tables and get the properties I want using LINQ

I have two related tables. Then I use LINQ to query Data.
this is my code
var items = await (from a in queryable
join b in _context.TUserGrant on a.UserNo equals b.UserNo
join c in _context.TProviderInfo on a.ProviderNo equals c.ProviderNo
orderby a.BillNo
select new
{
a.BillNo,
a.NotificeBillNo,
makeName = b.UserName,
a.MakeDate,
a.ProviderNo,
c.ProviderName,
a.CheckTime,
a.CheckAddress,
a.CheckName,
a.StatusTitle,
}).ToListAsync();
My problem is that I need all the columns of the first table, which is all the values of A.
I also need some columns from table B.
I wonder if there is an easy way to get these columns.
Instead of setting them one by one in the SELECT method.
You can try this
var items = await (from a in queryable
join b in _context.TUserGrant on a.UserNo equals b.UserNo
join c in _context.TProviderInfo on a.ProviderNo equals c.ProviderNo
orderby a.BillNo
select new
{
tabA = a,
makeName = b.UserName
}).ToListAsync();

Fluent LINQ query with multiple join conditions, one of which being a simple equals and another a less than comparison between ids

I have a SQL query which includes a left join and a group by- so far so good- my trouble arises from one of the join conditions not being a straight "equals to" and I'm lost where to go with LINQ.
I know multiple join conditions usually involves creating a couple of anonymous objects and comparing them, but when I add an "equal to" and "a greater" than into the mix, I've no idea how that applies.
Here's what I'd like the query to look like if I had invented LINQ, but I know the "and" in my join condition is invalid;
var query =
from csp in db.ChatSessionPersons
join cm in db.ChatMessages on
csp.ChatSessionId equals cm.ChatSessionId
and cm.Id > csp.LastReadChatMessageId
// (i know these should be the other way round,
// but for readability I present them like this!)
into j1
from j2 in j1.DefaultIfEmpty()
group j2 by csp.ChatSessionId into grouped
select new {
ChatSessionId = grouped.Key,
UnreadCount = grouped.Count(t => t.Id != null)};
Any ideas anyone?
You can convert the non-equality condition to a lambda Where on the group join result.
var query = from csp in db.ChatSessionPersons
join cm in db.ChatMessages on csp.ChatSessionId equals cm.ChatSessionId into cmj
select new {
ChatSessionId = csp.ChatSessionId,
UnreadCount = cmj.Where(cm => cm.Id > csp.LastReadChatMessageId).Count()
};
NOTE: I modified the query a bit to remove the group by which isn't needed if you are using a group join that has already grouped the matching results, and to remove the left join DefaultIfEmpty which also isn't needed when processing a group join with something like Count, unless you wanted to return an UnreadCount of 1 when there are no matches, in which case you should put DefaultIfEmpty() before Count().
Of course, you could use query comprehension in the sub-query:
var query = from csp in db.ChatSessionPersons
join cm in db.ChatMessages on csp.ChatSessionId equals cm.ChatSessionId into cmj
select new {
ChatSessionId = csp.ChatSessionId,
UnreadCount = (from cm in cmj where cm.Id > csp.LastReadChatMessageId select cm).Count()
};

Join list with table in linq

I have a problem. I get all data in var type variable and then want to apply a join with database table in code first approach. Facing problem, lot of search on internet and apply but failed.
var joinedData =
from menuGroup in _menuGroupMenusRepository.GetAll()
.Where(x => x.GroupId == input.GroupId)
join menus in _menuRepository.GetAll()
on menuGroup.MenuId equals menus.Id
join categSubcateg in _menuCategSubCategRepository.GetAll()
on menus.Id equals categSubcateg.MenuId
join categ in _menuCategoryRepository.GetAll()
on categSubcateg.CategoryId equals categ.Id
select new
{
CategoryId = categSubcateg.CategoryId,
CategoryName = categ.Category,
};
Now I want joinedData variable join with MainMenuSort table.
MainMenuSort table also have groupid and categoryid.
to perform join you just need to do as below
var q=(from jd in joinedData
join mms in dataContext.MainMenuSort
on jd.CategoryId equals mms.CategoryId
select jd).ToList();
if its datatable then
var q=(from jd in joinedData
join mms in dtMainMenuSort.AsEnumerable()
on jd.CategoryId equals mms.Field<int>("CategoryId")
select jd).ToList();

How to perform a left join with an additional filtering in LINQ to entities?

I have several tables, the main one is called DefectRecord, others are called DefectArea, DefectLevel...etc and the one called DefectAttachment. And this problem is about joining DefectRecord with other tables to get a ViewModel for further use. What the hard part I am facing is about the DefectAttachment table.
DefectRecord has a 1-to-many relation with DefectAttachment. While there may be NO attachment at all for one defect record, there may be multiple attachments.
Logically I tried to perform a left join among DefectRecord & DefectAttachment, but there is one more requiredment:
If there is multiple attachments, select ONLY the oldest one(i.e. the
one with oldest CreatedDate field value)
I am stuck at this requirement, how can I perform this with LINQ-to-Entities? Below is the code of what I have now:
var ret = (from dr in defectRecordQuery
join ft in filterQuery on dr.FilterID equals ft.FilterID
join l in levelQuery on dr.LevelID equals l.LevelID
join a in attachmentQuery on dr.DefectRecordID equals a.DefectRecordID into drd
from g in drd.DefaultIfEmpty()
select new DefectRecordViewModel
{
DefectRecordCode = dr.Code,
DefectAttachmentContent = g == null ? null : g.FileContent,
LookupFilterName = ft.FilterName,
}).ToList();
The *Query variable are the IQueryable object which get the full list of corresponding table.
Group your results by the Code and FilterName and then for the content take that of the item in the group that has the oldest date
var ret = (from dr in defectRecordQuery
join ft in filterQuery on dr.FilterID equals ft.FilterID
join l in levelQuery on dr.LevelID equals l.LevelID
join d in attachmentQuery on dr.DefectRecordID equals d.DefectRecordID into drd
from g in drd.DefaultIfEmpty()
group g by new { dr.Code, ft.FilterName } into gg
select new DefectRecordViewModel
{
DefectRecordCode = gg.Key.Code,
DefectAttachmentContent = gg.OrderByDescending(x => x.CreateDateTime).FirstOrDefault() == null? null: gg.OrderByDescending(x => x.CreateDateTime).FirstOrDefault().FileContent,
LookupFilterName = gg.Key.FilterName,
}).ToList();
If using C# 6.0 or higher then you can do:
DefectAttachmentContent = gg.OrderByDescending(x => x.CreateDateTime)
.FirstOrDefault()?.FileContent,

Linq InnerJoin 4 tables

My goal is to return a list of orders that only contain orderItems that are from a specific merchant. My current solution is to iterate through EVERY order, then through every order item and every listing. I imagine that is not the best practice, but I am having a hard time figuring out how to construct a single query to retrieve merchant specific orders.
I have 4 tables
Merchants(the id field being merchantID)
Orders(the id field is orderID)
orderItems(the id field is orderItemID, and FK listingID)
listings(the id field is listingID, and FK merchantID)
You can use .Any() to help you get to where you want:
var ordersFromMerchant = db.Orders
.Where(o => o.Items.Any(oi => oi.Listing.merchantID = 10);
I've made assumptions about the names of your navigation properties, but you should be able to adapt this if they don't match.
If you prefer the linq syntax, you can use:
var ordersFromMerchant = from o in db.Orders
join oi in db.orderItems on o.orderID equals oi.orderID
join l in db.listings on oi.listingID equals l.listingID
where l.merchantID = 10
select o;
I don't know your structure but something like this should work.
var query = from o in orders
join oi in orderItems on o.id equals io.orderID
join l in listings on oi.listingID equals l.id
where l.merchantID == merchantID
select o;

Categories