I need to group a list of questions by speaker using Linq:
public partial class Question
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
[ForeignKey("Speaker")]
public int Speaker_Id { get; set; }
public virtual Speaker Speaker { get; set; }
}
public partial class Speaker
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
}
I need to create a list of Questions Grouped By Speaker:
Question.Date | Speaker.Name | Question.Description
I get here but it's not working:
db.Questions.Select(c => new
{
c.Date,
c.Description,
c.Speaker.Id
})
.ToList()
.GroupBy(c => new { c.Id, c.Date, c.Description })
.Select(g => new Question
{
Date = g.Key.Date,
Speaker = db.Spkeakers.Where(o => o.Id == g.Key.Id).FirstOrDefault(),
Description = g.Key.Description
})
.ToList());
It's sorting by Date and not grouping by Speaker.
You're including Date (a DateTime type) in your group by clause. There's a good chance that each of your rows has a different date. If that property is true for your data, then grouping by date is essentially grouping by a unique identifier. Instead, maybe try to group by a less granular date by grouping by day, month, year, etc. Also, you could just group by speakerId and only order by date.
To order by speaker and date do this:
var result = db.Questions
.OrderBy(t => t.Speaker_Id)
.ThenBy(t => t.Date);
This is how you group by speakers and give a count:
var result = db.Questions
.GroupBy(t => t.Speaker_Id)
.Select(g => new { Speaker_Id = g.Key, count = g.Count() });
This is how you show this table ordered by date
var result = db.Questions
.OrderBy(t => t.Date)
.Select(item => new { Date = item.Date, Name = item.Speaker.Name, D = item.Description });
Note, if you want a list of Questions as your result then the last select is not needed.
Related
I have 3 tables and I'm trying to get a combined result with a sum of one field of them.
I'm working with C#, .NET, Entity Framework 7 and SQL Server.
I need to get the city's Name of each result, but I store the idCity
Brand table:
public byte IdBrand { get; set; }
public string Name { get; set; } = null!;
Bundles table:
public int IdBundle { get; set; }
public short IdCity{ get; set; }
public short IdBrand { get; set; }
public decimal? Volume { get; set; }
Cities:
public short IdCity { get; set; }
public string Name { get; set; } = null!;
I've tried this linq query and got almost the result I want but the city field is failing and I got stuck...
var volume = context.Bundles
.GroupBy(city => city.IdCity)
.Select(cad => new
{
CITY = context.Cities.Local.ToList().ElementAt(cad.Key)!.Name,
BRAND1 = cad.Where(c => c.IdBrand == 1).Sum(c => c.Volume),
BRAND2 = cad.Where(c => c.IdBrand == 19).Sum(c => c.Volume)
}).ToList();
I get this result that I expect but the CITY is not correct, I think because the cad.Key is not the same than Cities Index
I also tried:
context.Cities.ToList()
.Where(i => context.Bundles.Any(a=> i.IdCity == a.IdCity))
.Select(x=> x.Name)
CITY
BRAND1
BRAND2
LONDON
10.2
12
MOSCOU
11.4
1
PARIS
9.1
0.4
I guess that the cad.Key is not what I need to use to get the ElementAt Cities but how can I get the city .Name from another table in the Select? Or what is the best way to perform this query?
Try the following query, it should have better performance:
var query =
from b in context.Bundles
group b by b.IdCity into g
select new
{
IdCity = g.Key,
BRAND1 = g.Sum(c => c.IdBrand == 1 ? c.Volume : 0),
BRAND2 = g.Sum(c => c.IdBrand == 19 ? c.Volume : 0)
} into agg
join city in context.Cities on agg.IdCity equals city.Id
select new
{
CITY = city.Name,
BRAND1 = agg.BRAND1,
BRAND2 = agg.BRAND2
};
I have SQL query like this
SELECT T.*
FROM
(
SELECT ServiceRecords.DistrictId, Districts.Name as DistrictName, COUNT(Distinct(NsepServiceRecords.ClientRegNo)) AS ClientsServedCount
FROM ServiceRecords
INNER JOIN Districts ON ServiceRecords.DistrictId = Districts.ID
INNER JOIN NsepServiceRecords ON NsepServiceRecords.ServiceRecordId = ServiceRecords.Id
WHERE ServiceRecords.CreatedAtUtc >= #StartDate
AND ServiceRecords.CreatedAtUtc <= #EndDate
AND ServiceRecords.DistrictId = #DistrictId
GROUP BY ServiceRecords.DistrictId, Districts.Name
) AS T
ORDER BY T.DistrictName ASC, T.DistrictId
Query results:
DistrictId DistrictName ClientsServedCount
8d059005-1e6b-44ad-bc2c-0b3264fb4567 Bahawalpur 117
27ab6e24-50a6-4722-8115-dc31cd3127fa Gujrat 492
14b648f3-4912-450e-81f9-bf630a3dfc72 Jhelum 214
8c602b99-3308-45b5-808b-3375d61fdca0 Lodhran 23
059ffbea-7787-43e8-bd97-cab7cb77f6f6 Muzafarghar 22
580ee42b-3516-4546-841c-0bd8cef04df9 Peshawar 211
I'm struggling converting this to LINQ to entities query. I want to get same results (except District Id column) using LINQ.
I have tried like this, but not working as expected. Can somebody tell me what I'm doing wrong?
_dbContext.ServiceRecords
.Include(x => x.District)
.Include(x=>x.NsepServiceRecords)
.GroupBy(x => x.DistrictId)
.Select(x => new DistrictClientsLookUpModel
{
DistrictName = x.Select(record => record.District.Name).FirstOrDefault(),
ClientsServedCount = x.Sum(t=> t.NsepServiceRecords.Count)
});
Model classes are like this
public class BaseEntity
{
public Guid Id { get; set; }
}
public class NsepServiceRecord : BaseEntity
{
public DateTime CreatedAtUtc { get; set; }
public Guid ServiceRecordId { get; set; }
public string ClientRegNo { get; set; }
// other prop .......
public virtual ServiceRecord ServiceRecord { get; set; }
}
public class ServiceRecord : BaseEntity
{
public DateTime CreatedAtUtc { get; set; }
public string DistrictId { get; set; }
public virtual District District { get; set; }
public virtual ICollection<NsepServiceRecord> NsepServiceRecords { get; set; }
}
public class DistrictClientsLookUpModel
{
public string DistrictName { get; set; }
public int ClientsServedCount { get; set; }
}
I'm using Microsoft.EntityFrameworkCore, Version 2.2.4
EDIT
I have also tried like this
var startUniversalTime = DateTime.SpecifyKind(request.StartDate, DateTimeKind.Utc);
var endUniversalTime = DateTime.SpecifyKind(request.EndDate, DateTimeKind.Utc);
return _dbContext.NsepServiceRecords
.Join(_dbContext.ServiceRecords, s => s.ServiceRecordId,
r => r.Id, (s, r) => r)
.Include(i => i.District)
.Where(x => x.DistrictId == request.DistrictId
&& x.CreatedAtUtc.Date >= startUniversalTime
&& x.CreatedAtUtc.Date <= endUniversalTime)
.OrderBy(x => x.DistrictId)
.GroupBy(result => result.DistrictId)
.Select(r => new DistrictClientsLookUpModel
{
DistrictName = r.Select(x=>x.District.Name).FirstOrDefault(),
ClientsServedCount = r.Sum(x=>x.NsepServiceRecords.Count())
});
Another try,
from s in _dbContext.ServiceRecords
join record in _dbContext.NsepServiceRecords on s.Id equals record.ServiceRecordId
join district in _dbContext.Districts on s.DistrictId equals district.Id
group s by new
{
s.DistrictId,
s.District.Name
}
into grp
select new DistrictClientsLookUpModel
{
DistrictName = grp.Key.Name,
ClientsServedCount = grp.Sum(x => x.NsepServiceRecords.Count)
};
It takes too long, I waited for two minutes before I killed the request.
UPDATE
EF core have issues translating GroupBy queries to server side
Assuming the District has a collection navigation property to ServiceRecord as it should, e.g. something like
public virtual ICollection<ServiceRecord> ServiceRecords { get; set; }
you can avoid the GroupBy by simply starting the query from District and use simple projection Select following the navigations:
var query = _dbContext.Districts
.Select(d => new DistrictClientsLookUpModel
{
DistrictName = d.Name,
ClientsServedCount = d.ServiceRecords
.Where(s => s.CreatedAtUtc >= startUniversalTime && s.CreatedAtUtc <= endUniversalTime)
.SelectMany(s => s.NsepServiceRecords)
.Select(r => r.ClientRegNo).Distinct().Count()
});
You don't appear to be doing a join properly.
Have a look at this:
Join/Where with LINQ and Lambda
Here is a start on the linq query, I'm not sure if this will give you quite what you want, but its a good start.
Basically within the .Join method you need to first supply the entity that will be joined. Then you need to decide on what they will be joined on, in this case district=> district.Id, serviceRecord=> serviceRecord.Id.
_dbContext.ServiceRecords
.Join( _dbContext.District,district=> district.Id, serviceRecord=> serviceRecord.Id)
.Join(_dbContext.NsepServiceRecords, Nsep=> Nsep.ServiceRecord.Id,district=>district.Id)
.GroupBy(x => x.DistrictId)
.Select(x => new DistrictClientsLookUpModel
{
DistrictName = x.Select(record => record.District.Name).FirstOrDefault(),
ClientsServedCount = x.Sum(t=> t.NsepServiceRecords.Count)
});
I'm stuck on a Linq-to-SQL query. I have a table of items, with multiple items per user. I want to get the list of all the latest items per user. This is what I have:
var TheList = (from t in MyDataContext.TheTable
where t.SomeDate > DateTime.UtcNow.AddMonths(-3)
group t by t.UserID into TheUserGroups
from g in TheGroups
orderby g.SomeDate descending
select new SomeObject()
{
TheUserID = g.UserID,
.....
}).ToList();
The problem is that it's returning all the items of every user instead of the most recent element per user. What do I need to change to get the expected result?
Try following :
var TheList = (from t in MyDataContext.TheTable
where t.SomeDate > DateTime.UtcNow.AddMonths(-3)
orderby g.SomeDate descending
select new SomeObject()
{
TheUserID = g.UserID,
})
.GroupBy(x => x.TheUserID)
.Select(x => x.FirstOrDefault())
.ToList();
If I understood you correctly:
public class Order
{
public int OrderId { get; set; }
public int UserId { get; set; }
public DateTime Date { get; set; }
public string ItemName { get; set; }
}
table.Where(o => o.Date < DateTime.UtcNow.AddMonths(-3))
.GroupBy(o => o.UserId)
.Select(g => new { UserId = g.Key, LastOrder = g.OrderByDescending(o => o.Date).FirstOrDefault() })
.ToList();
I have List collection of Message objects.
public class Message
{
public int Id { get; set; }
public string Body { get; set; }
public string Sender { get; set; }
public DateTime Timestamp { get; set; }
}
I want to get only one message with most recent Timestamp for each sender. How do I do it using LINQ?
You need to group by Sender and then get the Max Timestamp from each group like:
var query = list.GroupBy(r => r.Sender)
.Select(grp => new
{
Sender = grp.Key,
RecentTimeStamp = grp.Max(r => r.Timestamp)
});
Or you can sort the TimeStamp in group by descending order and get the first element like:
var query = list.GroupBy(r => r.Sender)
.Select(grp => new
{
Sender = grp.Key,
RecentTimeStamp = grp.OrderByDescending(r => r.Timestamp).FirstOrDefault()
});
var q = from n in table
group n by n.Senderinto g
select g.OrderByDescending(t=>t.Timestamp).FirstOrDefault();
I have two classes:
class Customer
{
public string Name { get; set; }
public string ZipCode { get; set; }
public List<Order> OrderList { get; set; }
}
class Order
{
public string OrderNumber { get; set; }
}
Using LINQ, i want to get list of Orders group by ZipCode. If Zipcode "12121" has 10 customers and each has 2 orders then it should return me only one Zipcode with the list of 20 orders.
I am trying to do it like this but not able to figure out whats wrong
var orders = br.CustOrderList
.Select(r => new
{
r.ZipCode,
r.Name,
r.OrderList
})
.GroupBy(x => new { x.ZipCode, x.OrderList});
Any help please?
This should do what you want:
var orders = br.CustOrderList
.GroupBy(x => x.ZipCode)
.Select(g => new
{
ZipCode = g.Key,
Orders = g.SelectMany(x => x.OrderList)
});
var orders = br.CustOrderList
.Select(r => new
{
r.ZipCode,
r.Name,
r.OrderList
})
.GroupBy(x => x.ZipCode);
You just want to group by the ZipCode so just group by that
Ah yea just try
var order = br.CustOrderList
.GroupBy(x = x.ZipCode);
No need to select new items out of the list