Linq to Entity Group by Date - c#

Done a lot of research but still having a tough one with this. Consider a database which has a transactions table of "CreatedOn", "Amount", "Type". I need to be able to do an entity query to get transactions of a certain type grouped together by different date granularities (month / day etc).
So imagine a table with:
2011/1/22 $300 Deposit
2011/2/18 $340 Deposit
2011/3/6 $200 Other
2011/3/6 $100 Deposit
2011/3/7 $50 Deposit
I could have a query which would pull all deposits grouped by month so it could yield:
2011-1 $300 1deposit
2011-2 $340 1deposit
2011-3 $150 2deposits
How would I then adapt this query to be by day rather than month?
Here's my current block of code but I get an inner linq exception
Can't group on A1
var result = context.TransactionEntities.Where(d => d.Type == "Deposit")
.GroupBy(g => new { g.CreatedOn.Year, g.CreatedOn.Month })
.Select(g => new
{
DateKey = g.Key,
TotalDepositAmount = g.Sum(d => d.Amount),
DepositCount = g.Count()
}).ToList();
Note: I am currently using the MySql connector and I've read possibly this is a bug?

Func<DateTime, object> groupByClause;
if (groupByDay) groupByClause = date => date.Date;
else if (groupByMonth) groupByClause = date => new { date.Year, date.Month};
else throw new NotSupportedException("Some option should be chosen");
var result = data.Where(d => d.Type == "Deposit")
.GroupBy(groupByClause)
.Select(g => new { DateKey = g.Key,
TotalDepositAmount = g.Sum(d => d.Amount),
DepositCount = g.Count(),
});
Of course this should be checked whether linq-2-entities will accept it.

Check the code mentioned in my question: Group by Weeks in LINQ to Entities. It shows how you can group your data by days and months. Let me know if you have any other questions.

This is probably a bug in MySQL connector. My solution for that was to place .ToList() just before .GroupBy().

Related

Getting the Total Amount by grouping into month in Entity Framework

I need to write a SQL query in Entity Framework so as to get the total expense amount in array format (i.e I need to group the amounts month-wise).
I have tried this Entity Framework query but it didn't work out:
double[] Amountdate;
using (IncomeExpenseManagementDBEntities incomeExpenseManagementDB = new IncomeExpenseManagementDBEntities())
{
Amountdate = incomeExpenseManagementDB
.Expenses
.Select(e => e.Amount)
.Where(e => e.userID == IncomeExpenseTool.user.Id)
.GroupBy(e => e.Date.Month);
}
Appreciate any input into this
You need to do the Select last and then you'll need to aggregate the value by taking the Sum.
var amountsByMonth = incomeExpenseManagementDB.Expenses
.Where(e => e.userID == IncomeExpenseTool.user.Id)
.GroupBy(e => e.Date.Month)
.Select(grp => new
{
Month = grp.Key,
TotalAmount = grp.Sum(x => x.Amount)
})
.ToList();
That will result in the sum of the amount for each month that is represented in your DB (in other words if you have no data for the month of March you will not get a result for March).

Getting the count of most repeated records in Linq

I am working on an application in which I have to store play history of a song in the data table. I have a table named PlayHistory which has four columns.
Id | SoundRecordingId(FK) | UserId(FK) | DateTime
Now i have to implement a query that will return the songs that are in trending phase i.e. being mostly played. I have written the following query in sql server that returns me data somehow closer to what I want.
select COUNT(*) as High,SoundRecordingId
from PlayHistory
where DateTime >= GETDATE()-30
group by SoundRecordingId
Having COUNT(*) > 1
order by SoundRecordingId desc
It returned me following data:
High SoundRecordingId
2 5
2 3
Which means Song with Ids 5 and 3 were played the most number of times i.e.2
How can I implement this through Linq in c#.
I have done this so far:
DateTime d = DateTime.Now;
var monthBefore = d.AddMonths(-1);
var list =
_db.PlayHistories
.OrderByDescending(x=>x.SoundRecordingId)
.Where(t => t.DateTime >= monthBefore)
.GroupBy(x=>x.SoundRecordingId)
.Take(20)
.ToList();
It returns me list of whole table with the count of SoundRecording objects but i want just count of the most repeated records.
Thanks
There is an overload of the .GroupBy method which will solve your problem.
DateTime d = DateTime.Now;
var monthBefore = d.AddMonths(-1);
var list =
_db.PlayHistories
.OrderByDescending(x=>x.SoundRecordingId)
.Where(t => t.DateTime >= monthBefore)
.GroupBy(x=>x.SoundRecordingId, (key,values) => new {SoundRecordingID=key, High=values.count()})
.Take(20)
.ToList();
I have simply added the result selector to the GroupBy method call here which does the same transformation you have written in your SQL.
The method overload in question is documented here
To go further into your problem, you will probably want to do another OrderByDescending to get your results in popularity order. To match the SQL statement you also have to filter for only counts > 1.
DateTime d = DateTime.Now;
var monthBefore = d.AddMonths(-1);
var list =
_db.PlayHistories
.Where(t => t.DateTime >= monthBefore)
.GroupBy(x=>x.SoundRecordingId, (key,values) => new {SoundRecordingID=key, High=values.count()})
.Where(x=>x.High>1)
.OrderByDescending(x=>x.High)
.ToList();
I like the 'linq' syntax it's similar to SQL
var query = from history in _db.PlayHistories
where history.DateTime >= monthBefore
group history by history.SoundRecordingId into historyGroup
where historyGroup.Count() > 1
orderby historyGroup.Key
select new { High = historyGroup.Count(), SoundRecordingId = historyGroup.Key };
var data = query.Take(20).ToList();
You´re allmost done. Just order your list by the count and take the first:
var max =
_db.PlayHistories
.OrderByDescending(x=>x.SoundRecordingId)
.Where(t => t.DateTime >= monthBefore)
.GroupBy(x=>x.SoundRecordingId)
.OrderByDescending(x => x.Count())
.First();
This gives you a single key-value-pair where the Key is your SoundRecordingId and the value is the number of its occurences in your input-list.
EDIT: To get all records with that amount chose this instead:
var grouped =
_db.PlayHistories
.OrderByDescending(x => x.SoundRecordingId)
.Where(t => t.DateTime >= monthBefore)
.GroupBy(x => x.SoundRecordingId)
.Select(x => new { Id = x.Key, Count = x.Count() }
.OrderByDescending(x => x.Count)
.ToList();
var maxCount = grouped.First().Count;
var result = grouped.Where(x => x.Count == maxCount);
This solves the problem by giving you what you asked for. Your query in LINQ, returning just the play counts.
var list = _db.PlayHistories.Where(x => x.DateTimeProp > (DateTime.Now).AddMonths(-1))
.OrderByDescending(y => y.SoundRecordingId.Count())
.ThenBy(z => z.SoundRecordingId)
.Select(xx => xx.SoundRecordingId).Take(20).ToList();

grouping data by distinct dates using lambda

I have a Message table that has fields namely Creation_Date, Message_Count and Message_Cost. I'm trying to group data by the dates but I want the dates to not repeat and then sum the total number of messages and message cost on each date row.
I've tried using the below expression but that doesn't resolve the issue.
var query = db.Messages
.GroupBy(d => d.Creation_Date)
.OrderBy(d => d.Key)
.Select(g =>
new Report
{
TotalMessagesSent = g.Select(t=>t.Message_Count).Distinct().Sum(),
TotalCost = g.Select(p=>p.Customer_Price).Distinct().Sum(),
DateTime = (DateTime)g.Key
});
What am I doing wrong here?
I'm guessing your Creation_Date includes a timestamp? If so, use d.Creation_Date.Date instead to group by the date component only.

.NET when grouping records by hour impossible to use datetime in the select

I'm trying to group a list of records by hour and store the number of record for each hour. Here is my code :
DateTime firstTimeStamp = myRecords.DataBaseRecords.First().TimeStamp;
Statistics = myRecords.DataBaseRecords
.GroupBy(x => x.TimeStamp.Hour)
.Select(group => new GraphModel() { Date =firstTimeStamp.AddHours(group.Key), Value = group.Count() })
.ToList();
The problem is that when I'm on the select fuction, I cannot acces to the DateTime anymore so the field group.key contains a value between 0 and 24. I just need to group all the records by hour and foreach hour, I need to have the number of records in the Value parameter.
You have to group the data by absolute hours as of the first timestamp, i.e. the differences in hours calculated for each TimeStamp value:
Statistics = myRecords.DataBaseRecords
.GroupBy(x => DbFunctions.DiffHours(firstTimeStamp, x.TimeStamp) into g
.Select(g => new GraphModel
{
Date = g.FirstOrDefault().TimeStamp,
Value = g.Count()
};
If this is plain LINQ to objects (not Entity Framework) you can replace ...
DbFunctions.DiffHours(firstTimeStamp, x.TimeStamp)
... by
(x.TimeStamp - firstTimeStamp).TotalHours
If it's LINQ to SQL, use
SqlMethods.DateDiffHour(firstTimeStamp, x.TimeStamp)
Perhaps something like this may work out for you:
DateTime myDateTime = new DateTime(DateTime.Parse(firstTimeStamp).AddHours(group.Key).Ticks);
Question specific to answer above:
...Date = new DateTime(DateTime.Parse(firstTimeStamp).AddHours(group.Key))...

In a LINQ statement using closures, is it possible to specify a where clause within a closure?

Here is the problem I am attempting to solve. I have 3 database tables - Sales, Customer, and Time. One sales record links to exactly one customer record, and one time record. I want to, for the year 1996 only, look at the total sales by region (a customer is tied to a specific region), for the following further divisions: by holidays, non-holidays, weekdays, and weekends. Here is the query I have so far roughly, with what I am trying to add in comments.
var totalSales =
from s in sales
where s.Time.Year = 1996
group s by s.Customer.Region into g
select new { Region = g.Key,
Holidays = g.Sum(s => s.Total_Amount), // WHERE (s => s.Time.Holiday_flag = true)
NonHolidays = g.Sum(s => s.Total_Amount), // WHERE (s => s.Time.Holiday_flag = false)
Weekdays = g.Sum(s => s.Total_Amount), // WHERE (s => s.Time.Weekday_flag = true)
Weekends = g.Sum(s => s.Total_Amount)}; // WHERE (s => s.Time.Weekday_flag = false)
To do this, I would need to be able to, within each closure, further limit the results. Is this possible? Do I need to restructure the query? I could of course accomplish this by breaking it apart into 4 separate queries, but it would be really nice to do it in one.
Thanks.
Well, you could try this:
Holidays = g.Where(s => s.Time.Holiday_flag).Sum(s => s.Total_Amount),
NonHolidays = g.Where(s => !s.Time.Holiday_flag).Sum(s => s.Total_Amount),
Weekdays = g.Where(s => s.Time.Weekday_flag).Sum(s => s.Total_Amount),
Weekends = g.Where(s => !s.Time.Weekday_flag).Sum(s => s.Total_Amount)
or:
Holidays = g.Sum(s => s.Time.Holiday_flag ? s.Total_Amount : 0),
// etc

Categories