Including related fields in Linq group by query - c#

I'm trying to create a query that groups by a field in the first table, sums a first table field, and includes a single field from a second. I keep getting an error when attempting to refer to a field from the joined table.
Table1: Users
Id
DisplayName
Table2: TimeEntries
WeekEnding
UserId
Hours
Query:
from u in Users
join t in TimeEntries on u.Id equals t.UserId
group t by new {t.WeekEnding, t.UserId } into g
select new {WE = g.Key.WeekEnding, User = g.Key.UserId,
HoursTotal = g.Sum(s => s.Hours), DisplayName = g.First(n => n.DisplayName)}
I've tried many things but "DisplayName" is not a valid field.
Any help on this will be greatly appreciated. Thank you.

Here's a couple options.
from t in TimeEntries
group t by new {t.WeekEnding, t.UserId } into g
let firstT = t.First()
select new
{
WE = g.Key.WeekEnding,
User = g.Key.UserId
HoursTotal = g.Sum(x => x.Hours)
DisplayName = firstT.User.DisplayName
}
from t in TimeEntries
group t by new {t.WeekEnding, t.UserId } into g
let user = (from u in Users where u.UserId == g.Key.UserId select u).Single()
select new
{
WE = g.Key.WeekEnding,
User = g.Key.UserId
HoursTotal = g.Sum(x => x.Hours)
DisplayName = user.DisplayName
}

Enumerable.First(IEnumerable<TSource>, Func<TSource, Boolean>) returns the first element in a sequence that satisfies a specified condition.
But i assume that n.DisplayName is a string not a bool.
Try the other overload instead which simply returns the first element in a sequence:
DisplayName = g.First().DisplayName

Related

Converting SQL to LINQ with Multiple Tables and Group

There seem to be lots of questions about SQL to LINQ, but I can't seem to find examples with joined tables and grouping; specifically with a need to get data from multiple tables.
Take this simple SQL:
SELECT
s.showId, s.showName, v.venueName, Min(dateTime) startDate
FROM
shows s
INNER JOIN venues v ON s.venueId = v.venueId
INNER JOIN showDates d ON s.showId = d.showId
GROUP BY
s.showId
The best I can come up with is the following
var ungrouped = (
from s in db.Shows
join v in db.Venues on s.VenueId equals v.VenueId
join d in db.ShowDates on s.ShowId equals d.ShowId
select new { s, v, d }
).ToList();
var grouped = (
from s in ungrouped
group s by s.s.ShowId into grp
select new
{
showId = grp.Key,
name = (from g in grp select g.s.showName).FirstOrDefault(),
venue = (from g in grp select g.v.VenueName).FirstOrDefault(),
startDate = grp.Max(g => g.d.DateTime)
}
);
This works but it feels messy. I don't like:
It being split into two statements
Having to repeatedly write (from g in grp select ...).FirstOrDefault()
Bits like s.s.ShowId
How its vastly more lines of code than the SQL
This example is a simple one, it only gets worse when I have 5+ tables to join and 10+ columns to select.
Question: Is this the best way to do this, and I should just accept it; or is there a better way to write this query?
I am not sure if you are looking for something like this but it's a bit cleaner, it's not split in 2 statements and you might find it helpful. I couldn't use a dbcontext so I used lists to make sure the syntax is correct.
var res = Shows.Join(Venues,
show => show.VenueID,
venue => venue.VenueID,
(show, venue) => new { show, venue })
.Join(ShowDates,
val => val.show.ShowID,
showdate => showdate.ShowID,
(val, showDate) => new { val.show, val.venue, showDates = showDate })
.GroupBy(u => u.show.ShowID)
.Select(grp => new
{
showId = grp.Key,
name = grp.FirstOrDefault()?.show.showName,
venue = grp.FirstOrDefault()?.venue.VenueName,
startDate = grp.Max(g => g.showDates.DateTime)
});
we need to now realation beetwen them one to one or one to many , but not too far from this answer.
var GrouppedResult = Shows.Include(x=>x.Veneu).Include(x=>x.ShowDates)
.Where(x=>x.Veneu.Any()&&x.ShowDates.Any())
.GroupBy(x=>x.ShowId)
.Select(x=>///anything you want);
or
from show in Shows
join veneu in Veneu on veneu.VeneuId equals show.VeneuId
join showDates in ShowDates on showDates.ShowId=show.ShowID
group show by show.Id into grouppedShows
select new { ///what you want };

Linq using join and grouping

I'm trying to do something very simple.
I have two tables in my database that I would like to query using linq.
Table of Books, and table of GenreTypes. The result of this query would go to my web Api.
Here is a code snippet:
public List<BooksChart> GetBooksChart()
{
var results = from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select (z => new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
return results;
}
public class BooksChart
{
public string category_name;
public int value;
}
The results of the grouping "n" I would like to store them in BooksChart class to construct the Api.
This code is not compiling.
Previously, I was querying only one table of Books which I have divided into Books and GenreTypes.
My previous working code for querying Books was :
var results = _dbcontext
.Books
.GroupBy(x => x.GenreType)
.Select(z => new BooksPieChart
{
category_name = z.Key,
value = z.Count()
}).ToList();
return results;
EDIT
What I want to achieve in SQL is the following:
select count(*), g.Name
from books b, GenreTypes g
where b.GenreTypeId = g.Id
group by g.Name;
You are mixing the two syntax options of query and method. For query syntax you need to do the projection (select) like this:
return (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart {
category_name = n.Key,
value = n.Count()
}).ToList();
The format of (z =>....) is the declaration of the labmda passed to the Select method.
Site notes:
As #Rabbi commented, since you are using EF, consider properly defining navigation properties. It will make querying simpler.
Side note for the sql - consider using joins instead of multiple tables in the from: INNER JOIN ON vs WHERE clause
The parentheses must surround the whole query, like so:
var results = (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
The compilation error is due to this (z => which is not needed at all.

LINQ Select within a Select

I am trying to query a collection that contains Employee information. When I query that collection I would like to return an enumeration of objects where each object has two fields:
Name
ManagerName
(Note that every Manager is also an Employee!)
Now, here's the problem I am having. When I do a select within a select, the value of the ManagerName field that is returned on each object is:
System.Data.Common.Internal.Materialization.CompensatingCollection<string>
Here's the query:
var query =
from e in db.Employees
select new
{
Name = e.Name,
ManagerName =
from em2 in db.Employees
where (em2.EmployeeID == e.ManagerID)
select em2.Name
};
Specifically, when I look at the value of ManagerName, I see that it is an enumeration that yields a single item. And that the single item is a string that contains the name of the Manager. So, I think I'm close.
Question: How can I change my query so that instead it returns an enumeration of objects where each object simply has two string fields, Name and ManagerName?
Try this:
var query = from e in db.Employees
select new
{
Name = e.Name,
ManagerName = db.Employees
.Where(x => x.EmployeeID == e.ManagerID)
.Select(x => x.Name).SingleOrDefault()
};
However, if you correctly mapped your database with EF (which I suppose you are using), you should have a navigation property you can utilize:
var query = from e in db.Employees
select new
{
Name = e.Name,
ManagerName = e.Manager.Name
};
Looks like a self-join should work:
var query = from e in db.Employees
join m in db.Employees on e.ManagerID equals m.EmployeeID
select new
{
Name = e.Name,
ManagerName = m.Name
};

How to get Unique records via LINQ

var EmpRecList = (from ur in db.Users
join ug in db.UserGroups on ur.UserGroupID equals ug.UserGroupID
select new
{
lastName = ur.LastName,
userID = ur.UserID,
firstName = ur.FirstName,
userGroupName = ug.UserGroupNameLang1
}).Where(oh => oh.userGroupName.StartsWith(userCur.UserGroupName))
.OrderBy(x => x.lastName);
I have this code snippet. The problem here is, im getting 2 records with the same user ID. I would like to have a distinct record based on the User ID. Thanks. Tried using distinct method but no success.
You can use GroupBy and than get First record. It will get the first record which is in EmpRecList according to userid after ordering, but it will not ensure the result which you want to get.
Try this
var EmpRecList = (from ur in db.Users
join ug in db.UserGroups on ur.UserGroupID equals ug.UserGroupID
select new
{
lastName = ur.LastName,
userID = ur.UserID,
firstName = ur.FirstName,
userGroupName = ug.UserGroupNameLang1
})
.Where(oh => oh.userGroupName.StartsWith(userCur.UserGroupName))
.GroupBy(g => g.userID).Select(s => s.First()).ToList().OrderBy(x => x.lastName)
The problem here is that you wan't a distinct list of Users BUT your LINQ query is grouping some Users with more than one UserGroups. Performing a distinct on this will not give you a unique list because of different UserGroups.
You need to solve this in your where clause. It needs to be more specific. Instead of your predicate being StartsWith, rather use Equals.
var EmpRecList = (from ur in db.Users
join ug in db.UserGroups on ur.UserGroupID equals ug.UserGroupID
select new
{
lastName = ur.LastName,
userID = ur.UserID,
firstName = ur.FirstName,
userGroupName = ug.UserGroupNameLang1
}).Where(oh => oh.userGroupName.Equals(userCur.UserGroupName))
.OrderBy(x => x.lastName);
It would actually be better to compare the UserGroups by an ID instead of the name.
You can skip the join, so a user with more than one group won't appear twice. I Assumed there is no navigation property to groups, but if there is you can just use ur.UserGroups and you don't need the let definition.
var EmpRecList = (from ur in db.Users
let groups = db.UserGroups.Where(ug => ur.UserGroupID == ug.UserGroupID)
select new
{
lastName = ur.LastName,
userID = ur.UserID,
firstName = ur.FirstName,
userGroupNames = groups.Select(g => g.UserGroupNameLang1)
}).Where(oh => oh.userGroupNames.Any(n => n.StartsWith(userCur.UserGroupName)))
.OrderBy(x => x.lastName);

Filter LINQ entity include query

I have the following two tables
Groups
Id (int)
People
Id (int)
GroupId (int, Groups.Id)
IsSelected (bit)
This will return all Groups with all their members(People) in a single query
var grps = myDatabase.Groups.Include("People");
How can I write a single query that will return all Groups with People who has been selected(IsSelected = true)?
let me know if this works
var grps = myDatabase.Groups.Select(g=> new { g, people = g.People.Where(p=>p.IsSelected)});
You will want to use the 'join' method, like this:
(from g in myDatabase.Groups
join p in myDatabase.People on g.Id equals p.GroupId
where p.IsSelected == true
select g);
This will give you all groups where there are people selected.
OR check out .Where()
Something like
var grps = myDatabase.Groups.Include("People").Where(x => x.IsSelected);
//x => !x.IsSelected for false

Categories