Entity Framework: Get the last message from each conversation? - c#

My table Chats
id | sender | receiver | message | date
1 | 1 | 2 | Hello | 2015-12-08 20:00
2 | 2 | 1 | Hey | 2015-12-08 20:10
3 | 2 | 1 | You there? | 2015-12-08 21:00
4 | 1 | 3 | Yes | 2015-12-08 21:15
5 | 4 | 1 | Hey buddy | 2015-12-08 22:00
My controller
...
[HttpPost]
public async Task<JsonResult> ChatList(int person_id)
{
IEnumerable<Chat> chats = db.Chats.Where(p=>(p.sender==person_id||p.receiver==person_id));
return Json(chats);
}
How do I get the latest message from each conversation?

var result = db.Chats.GroupBy(c => (Math.Min(c.id1, c.id2), Math.Max(c.id1, c.id2)))
.Where(g => g.Key.Item1 == person_id || g.Key.Item2 == person_id)
.Select(g => g.OrderBy(c => c.date))
.Select(g => g.Last().message);
For efficiency you can replace OrderBy with an Aggregate that would fetch the latest element in O(n), but this illustrates the concept I think.

Related

Check if id's in a comma-separated string match any id's in another string array

I have two tables in DB Master Setup Approval and Order Details and I want check On any Master Approval Setup, this purchase order will go on relying CostCenter.
Table Master Setup Approval:
|-------|---------|-------------|-------------|---------------|---------------------|
| ID | Name | CRG_COM_ID| CRG_BRN_ID |ApprovalTypeId |CostCenter(string) |
|-------|---------|-------------|-------------|---------------|---------------------|
| 1 | Setup1 | 1 | 1 | 1 | "1,2,5,7" |
|-------|---------|-------------|-------------|---------------|---------------------|
| 2 | Setup2 | 1 | 1 | 1 | "1,3,6" |
|-------|---------|-------------|-------------|---------------|---------------------|
Table OrderDetails :
|-------|---------|-------------|-------------|------------------|
| ID | Name | CRG_COM_ID| CRG_BRN_ID |CostCenterID(long)|
|-------|---------|-------------|-------------|------------------|
| 1 | Item1 | 1 | 1 | 1 |
|-------|---------|-------------|-------------|------------------|
| 2 | Item2 | 1 | 1 | 7 |
|-------|---------|-------------|-------------|------------------|
This is my code:
var orderDetails = db.OrderDetails.Where(c => c.OrderId == orderId);
var costc = orderDetails.Select(c => c.CostCenterId.Value).ToList().ConvertAll<string>(delegate (long i) { return i.ToString(); });
var ApprovalProcess_Count12 = db.MasterSetupApproval.Where(x =>
x.CRG_COM_ID == order.CompanyId &&
(x.CRG_BRN_ID == null || x.CRG_BRN_ID == order.BranchId) &&
x.ApprovalTypeId == (int)ApprovalTypes.PO &&
x.CostCenter.Split(',').Select(aee => aee).Any(val => costc.Contains(val))
).ToList();
I am getting the following error:
LINQ to Entities does not recognize the method 'System.String[] Split(Char[])' method, and this method cannot be translated into a store expression.
Output should be:
|-------|---------|-------------|-------------|---------------|---------------------|
| ID | Name | CRG_COM_ID| CRG_BRN_ID |ApprovalTypeId |CostCenter(string) |
|-------|---------|-------------|-------------|---------------|---------------------|
| 1 | Setup1 | 1 | 1 | 1 | "1,2,5,7" |
|-------|---------|-------------|-------------|---------------|---------------------|
Assuming you are working with a badly-designed DB (as pointed out by Crowcoder) that comma-separated values should not be present in a database, you may refer this to tackle your way through.
HTH!

EF Core Groupby Get highest value of row

I have the following table:
| Id | ParentId | Version |
---------------------------
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 4 | 0 |
| 5 | 4 | 1 |
| 6 | 4 | 2 |
I am looking for a query (pref Linq to EF) where I can select the highest Version per group of ParentIds.
I currently have something like this:
await _context.Actions.GroupBy(a => a.ParentId)
.Select(s => s.Max(a => a.Version))
.ToListAsync();
The only problem I have is that only the version row is returned but I'd like the whole row to be returned per group.
You would need to order by descending and take the first in each group
await _context.Actions
.GroupBy(a => a.ParentId)
.Select(s => s.OrderByDescending(a => a.Version).First())
.ToListAsync();

Linq Return List of Objects after max effective From Date

Hiiya,
So trying to work out a query that returns a list of objects based on its max effective from date before a selected datetime.
Principle Example
+----+--------------+------------+----------------+
| id | Employee_Num | Money_Owed | Effective_From |
+----+--------------+------------+----------------+
| 1 | 1 | 10.11 | 20/10/2017 |
+----+--------------+------------+----------------+
| 2 | 1 | 15.11 | 24/10/2017 |
+----+--------------+------------+----------------+
| 3 | 1 | 20.11 | 30/10/2017 |
+----+--------------+------------+----------------+
| 4 | 2 | 6.89 | 20/10/2017 |
+----+--------------+------------+----------------+
| 5 | 2 | 9.89 | 25/10/2017 |
+----+--------------+------------+----------------+
| 6 | 2 | 12.89 | 29/10/2017 |
+----+--------------+------------+----------------+
so say I want to return each employees record as of 21/10/17 I would expect the below to be returned as a list of objects (Entities)
+----+--------------+------------+----------------+
| id | Employee_Num | Money_Owed | Effective_From |
+----+--------------+------------+----------------+
| 1 | 1 | 10.11 | 20/10/2017 |
+----+--------------+------------+----------------+
| 4 | 2 | 6.89 | 20/10/2017 |
+----+--------------+------------+----------------+
Then as of 24/10/2017
+----+--------------+------------+----------------+
| id | Employee_Num | Money_Owed | Effective_From |
+----+--------------+------------+----------------+
| 2 | 1 | 15.11 | 24/10/2017 |
+----+--------------+------------+----------------+
| 4 | 2 | 6.89 | 20/10/2017 |
+----+--------------+------------+----------------+
Im guessing the Query should be something along these lines but cant work out what it should be.
var qry = from t in db.Entity.Where(x => x.Effective_From <= as_of_date)
.OrderBy(x => x.Employee_Num)
select new Entity { *need rest of entity fields* ,effective_from = Max(e => e.Effective_From) };
Any help to finish off or point me in a different direction would be appreciated...
I believe what you'd be looking to do would involve a GroupBy expression to group by employee number, ordered by Employee number and then effective from descending, then take the first available combination.
var qry = db.Entity.Where(x => x.Effective_From <= as_of_date)
.OrderBy(x => x.Employee_Num)
.ThenByDescending(x => X.Effective_From)
.GroupBy(x => x.Employee_Num)
.Select(g => g.FirstOrDefault())
.ToList();
This is if you want the actual entities. I'd avoid doing something like "select new" for an entity as while this will contain the data of the relevant entities, they aren't the same reference as far as the context is concerned. .Select() would be used to retrieve an anonymous type suited for some logic or to populate a view model for a front end, or a DTO for an API result.

linq query to select top 3 from a group and get the average of those selected

I am struggling with a linq query.
I would like to:
Select for each schedule_Region_ids the bins that do not fail qc (QC=1)
Select the top 3 bins that do not have QC fail mark (1) (for example, bins 3 4 and 5 if no QC mark =1)
Average the 3 selected values to create 'BaselPeak' for each schedule_Region_id.
I have tried the following code:
var categories = drugonboard
.Where(p => p.Field<int?>("QC") != 1)
.OrderByDescending(x => x.Field<int>("bin")).Take(3)
.GroupBy(t => new { ID = t.Field<int>("schedule_region_id")})
.Select(g => new
{
schedule_region_id = g.Key.ID,
BaselPeak = g.Average(p => p.Field<double>("AnalytePeak"))
});
However I end up with the top 3 schedule_region_ids and not the average of the top three bins for each schedule_region_id. If i try to GroupBy first then i get anonomous type errors.
I have a dataset which looks like this:
id | bin | AnalytePeak | QC
1 | 1 | 620 |
1 | 2 | 1020 |
1 | 3 | 681 | 1
1 | 4 | 1190 |
1 | 5 | 1200 |
---------------------------
2 | 1 | 1020 |
2 | 2 | 1076 |
2 | 3 | 1190 |
2 | 4 | 1200 |
2 | 5 | 358 | 1
---------------------------
3 | 1 | 1020 |
3 | 2 | 1076 |
3 | 3 | 1190 |
3 | 4 | 1200 |
3 | 5 | 1358 |
You need to take 3 after grouping and after sorting the child records.
var categories = drugonboard
.Where(p => p.Field<int?>("QC") != 1)
.GroupBy(t => new { ID = t.Field<int>("schedule_region_id")})
.Select(g => new {
schedule_region_id = g.Key.ID,
BaselPeak = g.OrderByDescending(x => x.Field<int>("bin"))
.Take(3)
.Average(p => p.Field<double>("AnalytePeak"))
});
This assumes:
If all bins are QC=1, then you don't want the schedule_region_id parent at all and by top 3, you mean the highest 3 bin numbers.

Recent users query

I have 2 tables, for example let's assume that this is the data:
users table:
------------------------------
| user_id | username |
------------------------------
| 1 | danny |
| 2 | george |
| 3 | lana |
| 4 | kim |
| 5 | tim |
------------------------------
users_logins table:
-----------------------------------------------
| record_id | user_id | recorder |
-----------------------------------------------
| 1 | 3 | 2012-11-06 04:18:26 |
| 2 | 3 | 2012-11-06 04:31:05 |
| 3 | 2 | 2012-11-06 03:44:22 |
| 4 | 1 | 2012-11-06 04:18:58 |
| 5 | 1 | 2012-11-06 04:30:15 |
| 6 | 3 | 2012-11-06 04:31:05 | X
| 7 | 1 | 2012-11-06 05:53:47 |
| 8 | 1 | 2012-11-06 05:55:15 | X
| 9 | 4 | 2012-11-06 05:59:31 |
| 10 | 4 | 2012-11-06 06:12:55 | X
-----------------------------------------------
I want to show 3 recent logged in users, so the result will show only the rows that marked with X right to them, or in words unique recent logins.
How would the query look like?
Try this for linq to objects
(from login in users_logins.OrderByDescending(user => user.recorder)
from user in users
where user.user_id == login.user_id
select user).Distinct().Take(3)
This query
1. first sorts on date
2. then joins sorted login data with user data,
3. then take distinct users
4. and then finally take first 3 records.
Following is alternative query
from login in users_logins.OrderByDescending(user =>user.recorder).GroupBy(user=>user.recorder).SelectMany( users=>users.First()).Take(3)
from user in users
where user.user_id == login.user_id
select user
This query
1. first sorts
2. then groups on user_id
3. then take first 3 records with different user id,
4. then joins with users data.
I think this is what you want.
var resentLogedInUsers =
users
.Join(
users_logins,
u => u.user_id,
l => l.user_id,
(u, l) => new
{
User = u,
Login = l.recorder
})
.GroupBy(j => j.User)
.Select(l => new {User = l.Key, LastLogin = l.Max(d => d.Login)})
.OrderByDescending(r => r.LastLogin)
.Take(3);

Categories