So you have a sequence of Projects
, and a sequence of Issues
. Furthermore you have a sequence of TimeEntries
where every TimeEntry
belongs to exactly one Project
using foreign key TimeEntry.ProjectId
. Every TimeEntry
also belongs to exactly one Issue
using foreign key TimeEntry.IssueId
You want to join these three tables on their primary and foreign keys. You only want to keep some elements of the joined results (Where). The remaining elements should be grouped into groups with same name
, id
, subject
and spentOn
. Finally you want to select some properties from every group with its elements.
If you have something similar to entity framework, your Project will have a virtual collection of TimeEntries and every TimeEntry has a virtual reference to a Project. If that method is used, entity framework will understand that a join is needed. You can usee the references between the tables.
That is discussed later. First the method using the join three tables
Method: Join three tables
Normally I would use Method syntax. However method syntax looks hideous if you join three tables. Those are the only times I use Query syntax.
I do this in smaller steps. Feel free to make it one big linq
var joinedItems = from timeEntry in timeEntries
join project in project on timeEntry.ProjectId equals project.Id
join issue in issues on timeEntry.IssueId equals issue.Id
select new
{
TimeEntry = timeEntry,
Project = Project,
Issue = Issue,
};
Keep only some of the joined items:
int currentMonth = DateTime.Now.Month;
var subsetJoinedItems = joinedItems.Where(joinedItem =>
(joinedItem.Project.Id == 26 || joinedItem.Project.Id == 27)
&& joinedItem.Issue.Subject.Contains("zlecane") // consider ignore case
&& joinedItem.TimeEntry.SpentOn.Month == currentMonth);
Group the resulting elements into groups of same [name, id, subject, spent_on]:
var groups = subsetJoinedItems.GroupBy(joinedItem => new
{
ProjectId = joinedItem.Project.Id,
ProjectName = joinedItem.Project.Name,
Subject = joinedItem.Issue.Subject,
Spent_on = joinedItem.TimeEntry.SpentOn,
});
Finally, from every group select the items you want to keep:
var result = groups.Select(group => new
{
ProjectId = group.Key.ProjectId,
ProjectName = group.Key.ProjectName,
Subject = group.Key.Subject,
SpentOn = group.Key.SpentOn,
// remaining properties: SQL query seems incorrect
...
});
Using Class Relationships as in entity framework
If you have something similar to entity framework, then your one-to-many relationships would have been implemented using collections:
class TimeEntry
{
public int Id {get; set;}
// every TimeEntry belongs to exactly one Project using foreign key
public int ProjectId {get; set;}
public virtual Project Project {get; set;}
// every TimeEntry belongs to exactly one Issue using foreign key
public int IssueId {get; set;}
public virtual Issue Issue {get; set;}
}
If you have something like this, you don't have to do the joins yourself, entity framework would understand it:
var result = TimeEntries.Where(timeEntry =>
(timeEntry.ProjectId == 26 || timeEntry.ProjectId == 27)
&& timeEntry.Issue.Subject.Contains("zlecane") // consider ignore case
&& TimeEntry.SpentOn.Month == currentMonth)
.GroupBy(timeEntry => new
{
ProjectId = joinedItem.Project.Id,
ProjectName = joinedItem.Project.Name,
Subject = joinedItem.Issue.Subject,
Spent_on = joinedItem.TimeEntry.SpentOn,
})
.Select(group => new
{
ProjectId = group.Key.ProjectId,
ProjectName = group.Key.ProjectName,
Subject = group.Key.Subject,
SpentOn = group.Key.SpentOn,
...
});