I need some help with LINQ-2-SQL in order to group some blog posts by a year and month.
Basically, I have a collection of blog posts that have the following properties
Id
Title
Date
I want to be able to iterate through each year, then iterate through every month of that year, and finally, iterate through every blog post within that month. Something like
2011
April (show number of posts)
Random Post 1
Random Post 2
May (show number of posts)
Random Post 2
etc...
Is there a way I can do this with a single LINQ query, using the group by clause?
Here's as far as I've got
var groupedBlogPosts = (from p in blogPostsFiltered
group p by new { month = p.Date.Month, year = p.Date.Year } into d
select new { postDate = string.Format("{0}/{1}", d.Key.month, d.Key.year), postCount = d.Count() });
I haven't actually tested this, but it looks like a start, wrote this by looking at this msdn article:
var groupedBlogPosts =
from p in blogPostsFiltered
group p by p.Date.Year into yg
select
new
{
Year = yg.Key,
MonthGroups =
from o in yg
group o by o.Date.Month into mg
select new { Month = mg.Key, Posts = mg }
};
Look under the GroupBy - Nested heading.
Here's an alternative if you don't want to do the projections and leave them as groupings.
var groupedBlogPosts =
from post in blogPostsFiltered
group post by new { post.Date.Year, post.Date.Month } into grouped
group grouped by new { grouped.Key.Year };
Related
I'm pretty new to C# and LINQ and I'm trying get a list of emails that holds the sum of emails, attachments and user's (the one's that sent the email).
So my current Problem is the Output of my Query is false. The number of email's is equal to the number of attachment's which obvious is wrong.
My Query:
var monthQuery = from em in dbEdoka.email
join ema in dbEdoka.email_attachment on em.id equals ema.email_id into e
from e2 in e.DefaultIfEmpty()
group e2 by em.erstellt_am.Month into grouped
select new Entities.Month
{
NameOfMonth = grouped.FirstOrDefault().erstellt_am.ToString(),
NumberOfMails = grouped.Distinct().Count(m => m.email_id != null).ToString(),
NumberOfAttachments = grouped.Count(a => a.id != null).ToString(),
NumberOfUsers = grouped.Select(u => u.erstellt_von).Distinct().Count().ToString()
};
months = monthQuery.ToList();
Months = CollectionViewSource.GetDefaultView(months);
As you can see I had to take m.email_id from dbEdoka.email_attachment instead of m.id from dbEdoka.email because it wasn't avaliable (don't know why...).
Yet I have to count "NumberOfMails", "NumberOfAttachments" and "NumberOfUsers".
Thank you!
I have a db table with columns like
DaysInTrade, PercentGain
i want a sum of PercentGain grouped by DaysInTrade
so:
DaysInTrade,PercentGain
1, 5
1,6
2,4
Would give me:
DaysInTrade, Sum
1, 11
2,4
I was not able to get intellisense working in the "select new" part unless I used something like Sum or Select. the DIT column below shows up blank no matter if I do ts.DaysInTrade, or gts.DaysInTrade
var partialQuery = (from ts in context.TradeSnapshots
group ts by ts.DaysInTrade
into gts
select
new
{
profit = gts.Sum(s => s.PercentGain),
DIT = gts.Select(ts=>ts.DaysInTrade)
});
dataGridViewTradeSnapshots.DataSource = partialQuery.ToList();
You want to use the key on the grouping. See the documentation on IGrouping<TKey,TElement>
var partialQuery = (from ts in context.TradeSnapshots
group ts by ts.DaysInTrade
into gts
select
new
{
profit = gts.Sum(s => s.PercentGain),
DIT = gts.Key
});
dataGridViewTradeSnapshots.DataSource = partialQuery.ToList();
I'm building an ArchivesController for the open source asp.net MVC-3 blog platform FunnelWeb. We have an model called "Entry" which represents a blog entry which has a DateTime property called "Published" for when this entry was published. The purpose of the proposed ArchivesController is to create a wordpress-like archives link table that shows a descending list of all years and months for which we have posts with links to an archive index like '/archive/2011/9' and a count for the number of posts in the year/month.
ex:
December 2011 (2 posts)
November 2011 (4 posts)
October 2011 (1 post)
I'm not experienced with NHibernate and so wrote the initial query using linq like this:
public class GetArchiveDatesQuery : IQuery<ArchiveDate>
{
public System.Collections.Generic.IEnumerable<ArchiveDate> Execute(ISession session, IDatabaseProvider databaseProvider)
{
var criteria = session.QueryOver<Entry>();
var archiveDates = from entry in criteria.List<Entry>()
group entry by new { entry.Published.Year, entry.Published.Month } into entryGroup
orderby entryGroup.Key.Year descending, entryGroup.Key.Month descending
select new ArchiveDate()
{
Year = entryGroup.Key.Year,
Month = entryGroup.Key.Month,
EntryCount = entryGroup.Count()
};
return archiveDates;
}
}
Where ArchiveDate is a new model I created to encapsulate the year-month-count information from this query.
This works, but I'd prefer to push the work off to SQL instead of doing the grouping and sorting in C#. I imagine on an active blog that has been around for several years with hundreds or thousands of posts would be much better off to do this in SQL so we don't return unnecessary data (like the entry content).
My question is how we can accomplish the above LINQ statement in an NHibernate fashion which results in the grouping/sorting occurring in SQL. I imagine it will involve some Criteria->Projection->Transformation sort of process.
The part I'm stuck on is accessing the month portion and year portion of the DateTime property for grouping and sorting which is currently accessed by the .Net DateTime object.
The blog engine is using NHibernate version 3.2.0.4000.
Update:
var query = from e in session.Query<Entry>()
group e by new { e.Published.Year, e.Published.Month } into entryGroup
select new
{
entryGroup.First().Published,
EntryCount = entryGroup.Count()
};
var archiveDates = from a in query.AsEnumerable()
orderby a.Published.Year descending, a.Published.Month descending
select new
{
Year = a.Published.Year,
Month = a.Published.Month,
EntryCount = a.EntryCount,
};
Original Answer:
could you try NHibernates LINQ-provider first and post the errors?
using NHibernate.Linq;
var archiveDates = from entry in session.Query<Entry>()
group entry by new { entry.Published.Year, entry.Published.Month } into entryGroup
orderby entryGroup.Key.Year descending, entryGroup.Key.Month descending
select new ArchiveDate
{
Year = entryGroup.Key.Year,
Month = entryGroup.Key.Month,
EntryCount = entryGroup.Count()
};
I am working with stacked headers on a Telerik WPF data grid. The way I was approaching the problem was to create a flattened view of my data. For example.
it would display like the following:
Person A | Person B | Person C
Month | Earning Month|Earning Month|Earning
I have a collection of persons containing their respective lists of months and earning.
Therefore I want to bring each person on the same level or in easier terms, I want to create a single list with all the months and earnings available horizontally.
What would the best way to approach this?
I am unsure if you want a list that has the people in it multiple times or just the list with the earnings. I give you a couple solutions based upon the differing requirements and the guesses that I had to make about your class structure.
First assuming you want a list of where each item has a person, the month, and the earnings and you have the structure of a person class with two contained lists (one for earnings and one for months that are synchronized on indexes you can use a variant of the following LINQ query to create the enumeration
var flatList = from person in persons
from month in person.Month.Select((month, index) => new {Month = month, Index = index})
from amount in person.Amount.Select((amount, index) => new { Amount = amount, Index = index })
where month.Index == amount.Index
select new { Name = person.Name, Month = month.Month, Amount = amount.Amount};
If instead you wanted just the month and amounts given the same structure you'd write the following:
var flatList = from person in persons
from month in person.Month.Select((month, index) => new {Month = month, Index = index})
from amount in person.Amount.Select((amount, index) => new { Amount = amount, Index = index })
where month.Index == amount.Index
select new { Month = month.Month, Amount = amount.Amount};
If instead you had a list of persons that has an earnings object which matches the month and the amount you could write something like this:
var flatList = from person in persons
from earning in person.Earnings
select new { Pers = person.Name, Month = earning.Month, Amount = earning.Amount };
Or again if you didn't want the person you'd use:
var flatList = from person in persons
from earning in person.Earnings
select new {Month = earning.Month, Amount = earning.Amount };
I hope one of those is what you were looking for, otherwise please clarify your question further because I misunderstood.
As others have said, it's difficult to be specific without seeing more of your code. But, in general, flattening collections is the sort of thing that the SelectMany() LINQ extension method is made for.
I'm new to LINQ to SQL and I would like to know how to achieve something like this in LINQ:
Month Hires Terminations
Jan 5 7
Feb 8 8
Marc 8 5
I've got this so far, and I think there is something wrong with it but I'm not sure:
from term1 in HRSystemDB.Terminations
group term1 by new { term1.TerminationDate.Month, term1.TerminationDate.Year } into grpTerm
select new HiresVsTerminationsQuery
{
Date = Criteria.Period,
TerminationsCount = grpTerm.Count(term => term.TerminationDate.Month == Criteria.Period.Value.Month),
HiresCount = (from emp in HRSystemDB.Persons.OfType<Employee>()
group emp by new { emp.HireDate.Month, emp.HireDate.Year } into grpEmp
select grpEmp).Count(e => e.Key.Month == Criteria.Period.Value.Month)
});
Thanks in advance.
I'm not quite sure where does the Criteria.Period value come from in your sample query.
However I think you're trying to read both hires and terminations for all available months (and then you can easily filter it). Your query could go wrong if the first table (Termination) didn't include any records for some specified month (say May). Then the select clause wouldn't be called with "May" as the parameter at all and even if you had some data in the second table (representing Hires), then you wouldn't be able to find it.
This can be elegantly solved using the Concat method (see MSDN samples). You could select all termniations and all hires (into a data structure of some type) and then group all the data by month:
var terms = from t in HRSystemDB.Terminations
select new { Month = t.TerminationDate.Month,
Year = term1.TerminationDate.Year,
IsHire = false };
var hires = from emp in HRSystemDB.Persons.OfType<Employee>()
select new { Month = emp.HireDate.Month,
Year = emp.HireDate.Year
IsHire = true };
// Now we can merge the two inputs into one
var summary = terms.Concat(hires);
// And group the data using month or year
var res = from s in summary
group s by new { s.Year, s.Month } into g
select new { Period = g.Key,
Hires = g.Count(info => info.IsHire),
Terminations = g.Count(info => !info.IsHire) }
When looking at the code now, I'm pretty sure there is some shorter way to write this. On the other hand, this code should be quite readable, which is a benefit. Also note that it doesn't matter that we split the code into a couple of sub-queries. Thanks to lazy evalutation of LINQ to SQL, this should be executed as a single query.
I don't know if it shorter but you can also try this version to see if it works better with your server. I don't know exactly how these two answers turn into SQL statements. One might be better based on your indexs and such.
var terms =
from t in Terminations
group t by new {t.Month, t.Year} into g
select new {g.Key, Count = g.Count()};
var hires =
from p in Persons
group p by new {p.Month, p.Year} into g
select new {g.Key, Count = g.Count()};
var summary =
from t in terms
join h in hires on t.Key equals h.Key
select new {t.Key.Month, t.Key.Year,
Hires = h.Count, Terms = t.Count};