LINQ Select within a Select - c#

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
};

Related

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.

get the value of a query to be part of another query

I'm trying to use the value of signerID to be part of the where clause in another query, but getting red underline with error: "delegate does not take 1 argument". Basically I want to use the value of signerID query (value = 15) in the where clause of the query allDepartments. Here's the code:
using (dbPSREntities10 myEntities = new dbPSREntities10())
{
int theStatus = Convert.ToInt32(ddlChangeStatus.SelectedValue);
string userName = HttpContext.Current.User.Identity.Name;
var signerID = (from refAuthSigner in myEntities.refAuthSigners.Where(x => x.NetworkUsername == userName)
select new
{
refAuthSignerID = refAuthSigner.refAuthSignerID
}); <---- here I should have the value 15
var allDepartments = (from tbProject in myEntities.tbProjects.Where(x => x.refAuthSignerID == signerID)<-- and I want to use the value of signerID (15) here
Is this way off? I can hardcode 15 in place of signerID in the where clasue and everything works fine. Thanks in advance!
I suggest you to join tables on singer id:
var allDepartments =
from p in myEntities.tbProjects
join s in myEntities.refAuthSigners
on p.refAuthSignerID equals s.refAuthSignerID
where s.NetworkUsername == userName
select p;
This query will select only those project which have singer id equal to id of singer with provided name.
Of course, you can do all in two queries - get id by first query, and them use id in second query:
int singerId = myEntities.refAuthSigners
.Where(s => s.NetworkUsername == userName)
.Select(s => s.refAuthSignerID)
.FirstOrDefault(); // execute first query
if (singerId == 0)
return; // singer not found
// build second query
var allDepartments = myEntities.tbProjects
.Where(p => p.refAuthSignerID == signerID);

linq query one to one

trying to get the concert name, date and venue (which is stored on another table)
but it doesn't seem to be working, but it keeps pulling back null.
var test = from f in db.Concert
where f.Name.StartsWith(concertName)
select new
{
f.Name,
f.StartDateTime,
venues = from v in db.Venues
where v.ID == f.VenueID
select v.Address
}
Edit:
The relationship between Venue and Concert is that Concert has a VenueID that relates to the Venue ID. I need to pass a string back. something like
foreach (var e in test)
{
html += "<div> e.Name +":" + e.Address </div>;
}
You can use group join to get all Venues related to Concert
var test = from f in db.Concert
join v in db.Venues on f.VenueID equals v.ID into g
where f.Name.StartsWith(concertName)
select new
{
f.Name,
f.StartDateTime,
Venues = g.Select(x => x.Address)
};
Usage of results:
foreach (var e in test)
{
// e.Name
// e.StartDateTime
foreach(var address in e.Venues)
// address
}
It looks like it's safe to assume a 1:1 relationship between Concerts and Venues. Given that, you can use join instead.
var test = from f in db.Concert
join v in db.Venues on v.ID equals f.VenueID
where f.Name.StartsWith(concertName)
select new
{
f.Name,
f.StartDateTime,
v.Address
};
If you have a foreign key relationship set up, You don't need to manually query for venues. In that case you get the venues using f.Venue.

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);

Including related fields in Linq group by query

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

Categories