I am trying to figure out how to perform a conditional query on an Employees table to bring back all of their assigned Projects, but the caveat I don't quite understand on how to implement is that for every Employee, there is 0 to 1 EmployeeAssistant (self-joining entity). So when I select EmployeeID=2 and it has an EmployeeAssistantID=5, I would like to display all of the Projects for both of these individuals, i.e. the main Employee (EmployeeID=2) and their assistant (EmployeeID=5).
The tables look like:
Employees
- EmployeeID -- (Pkey)
- EmployeeAssistantID -- (Fkey to Employees.EmployeeID)
- other fields
-
Projects
- ProjectID -- (PKey)
- EmployeeID -- (Fkey to Employees.EmployeeID)
- other fields
I attempted the following in LINQ:
var projects = Projects.Include(proj => proj.Employee)
.Select(x => new
{
proj.ProjectID,
proj.ProjectName,
proj.Employee.Name
// ... not sure how to bring back another layer of projects for the EmployeeAssistant?
})
You can use the following Linq query to get the data you want:
var query = from e in Employees
join a in Employees on e.EmployeeAssistantID equals a.EmployeeID
where e.EmployeeID == 2
select new
{
EmployeeID = e.EmployeeID,
AssistantID = a.EmployeeID,
EmployeeProjects = Projects.Where(p => p.EmployeeID == e.EmployeeID),
AssistantProjects = Projects.Where(p => p.EmployeeID == a.EmployeeID)
};
The anonymous type returned by the query contains all of the data from both employee entities, as well as all of the Project data of each employee (some can be the same, others might differ).
I believe you mean to do the following:
var employeeIDs = new[] { myEmployee.EmployeeID, myEmployee.EmployeeAssistantID };
var projects = Projects.Where(p => employeeIds.Contains(p.EmployeeID));
This will grab all projects that both the employee and the assistant have done, given a previously grabbed Employee record (which I've called myEmployee).
Related
I've found plenty of info on how to select multiple result sets with stored procedures but nothing substantial on how to do so with a linq query.
For example, I can do sub-queries that return mulitple sets of results with something like
var query = (from school in context.Schools
where school.id == someId
select new
{
subSetA = (from student in context.Students
select student).ToList(),
subSetB = (from building in context.Buildings
select building).ToList(),
}).First();
query.subSetA; //Access subSetA
query.subSetB; //Access subSetB
Which works fine, but what if I just want to select both subSetA and subSetB without querying against the school table? I want to select two separate sets of data that gets sent to the server in one query.
Any information as to how to do this with EF 6 would be great.
Well, I'm sure there are many ways to do this, but if you want to avoid introducing a third DbSet into the mix...
var query = (from s in context.Students.Take(1)
select new
{
subSetA = context.Students.ToList(),
subSetB = context.Buildings.ToList(),
})
Then, you can use query.ToList() or maybe using query.Load() and working with context.Students.Local, etc. would work.
I am not sure how possible this is but I have two tables and I want to grab a value from table 2 via the value of table 1.
Table 1 has the a Foreign Key called "rank" which is an int. Table 2 has a value called "name" which is a string. Now Table 1's "rank" correlates to Table 2's "ID".
So when I say
var result =
db.Table1.Select(x => new { x.name, x.rank }).ToList();
//Bob - 2
I really want to say something like
var result =
db.Table1.Select(x => new { x.name, Table2.rank.Where(ID == x.rank) }).ToList();
//Bob - Gold
I am still new to LINQ though and I am not sure how to get rank's string value from the other table within a query like this.
EDIT
Tables I am using and their relational values.
User: ID (PK), s1elo (FK to PastElos), champ (FK to ChampionList), elo (FK to EloList)
PastElo: ID (PK), Rank
ChampionList: ID (PK), name
EloList: ID (PK), Rank
Working example for Users and PastElo
var result =
db.Users.Join(db.PastEloes,
x => x.s1elo, y => y.ID, (x, y)
=> new { y.Rank, x.name, x.other_items_in_Users }).ToList();
Note: PastElo is PastEloe's due to EF making everything plural when I synced up my DB, thus why User is also Users, I think that is referred to as the "context".
You could try something like the following:
var result = db.Table1.Join(db.Table2,
x=>x.rank,
y=>y.ID,
(x,y) => new { x.rank, y.Name }).ToList();
In the above linq query we make a Join between the two tables, Table1 and Table2 based on the association and then we select that we want.
Another way you could try to write this query would be the following:
var result = (from t1 in db.Table1
join t2 in db.Table2
on t1.rank equals t2.ID
select new { t1.rank, t2.Name, }).ToList();
Another way to do this would be to include your Database relationships in your C# entities. You could use EntityRef here. See the following documentation:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/linq/how-to-map-database-relationships
Consider a (simplified) table structure like this:
[USERS]
EMPID
NAME
[APPOINTMENTS]
(FK_APPT_USER) EMPID
APPTTYPEID
COMPLETE
Each user can have 0..* appointments, each of which can be one of many APPTYPEID's, and can either be complete or not complete.
I want to filter the result set of a IQueryable[USER] query such that it only includes USERS who have an appt of some typeID (say 1) and where the COMPLETE field is in a list of values. I'm doing this as part of a gridview filter that allows users to select either to show only completed or not completed users for particular appointment types.
List<string> vals = new List<string> {"Y","N"}
//maybe the user has only selected Y so the above list only contains 1 element
var qry = ctx.USER.Where(x=> vals.Contains( ? ));
//bind etc
This is really easy to do if the values I'm comparing against the list are in a 1-1 relationship with the USER object, for example:
var qry = ctx.USER.Where(x=> vals.Contains(x.NAME));
But I don't understand how to do it with a 1-many relationship like with my appointments table, it's getting me all brain-tied trying to conceptualize the entity sql for it. Can anybody explain how to do this?
qry = ctx.USER.Where(u => u.APPOINTMENTS
.Where(a => a.APPTYPEID == 1)
.Any(a => vals.Contains(a.COMPLETE)));
UPDATE (added returning those users, which do not have appointments at all)
qry = ctx.USER.Where(u =>
!u.APPOINTMENTS.Any() ||
u.APPOINTMENTS.Any(a => a.APPTYPEID == 1 && vals.Contains(a.COMPLETE)));
I have three tables namely "Projects", "Platforms", "Details".
I have collected detail from the Projects table, then Platforms table and Details table. From the details table we may get the multiple values or single value.
I have written the Linq query like below:
using (PEntities CSProject = new PEntities())
{
projectId = (from _project in CSProject.Projects
where _project.ProjectName == project
select _project.ProjectId).SingleOrDefault();
platformId = (from platformID in CSProject.Projects
where platformID.ProjectName == project
select platformID.PlatformId).SingleOrDefault();
platformName = (from platfrmName in CSProject.Platforms
where platfrmName.PlatformId == platformId
select platfrmName.PlatformName).SingleOrDefault();
cSProjectId = (from _csproject in CSProject.Details
where _csproject.ProjectId == projectId
select _csproject.CsprojectId).ToList<long?>();
}
Can you please help me out to write the above all query in a single single line?
You could create a correlated subquery for each of the values that you want to get in the select statement, like so (in SQL):
select
(select ProjectId from Projects where ProjectName = #project) as ProjectId,
(select PlatformId from Projects where ProjectName = #project) as PlatformId,
...
In SQL, the above doesn't need a table to query against (it will return one row), but you'll need something in LINQ-to-Entities, which will force you to query against a context, make sure there is at least one value in the table, etc.
Needless to say, it's not worth it, and while you'll gain less calls to the database, the actual query that is sent will be very complex.
That said, you're better off doing one of two things. The first is to create a stored procedure that creates the correlated subqueries and returns one record like above and call that from LINQ-to-Entities.
The other, if you are really looking for one record that stores these values is to store them in a type. Here's how to do it in an anonymous type:
using (PEntities CSProject = new PEntities())
{
var results = new {
ProjectId = (
from _project in CSProject.Projects
where _project.ProjectName == project
select _project.ProjectId).SingleOrDefault(),
PlatformId = (
from platformID in CSProject.Projects
where platformID.ProjectName == project
select platformID.PlatformId).SingleOrDefault(),
PlatformName = (
from platfrmName in CSProject.Platforms
where platfrmName.PlatformId == platformId
select platfrmName.PlatformName).SingleOrDefault();
CsProjectId = (
from _csproject in CSProject.Details
where _csproject.ProjectId == projectId
select _csproject.CsprojectId).ToList<long?>();
};
}
You won't see a reduction in calls to the database, but if what you're going for is a single record with these values, then this will achieve it.
Suppose I have two tables: Category and Product. I want use linq-to-sql to select all categories (with products) that have duplicated products.
My query goes like this:
from p in db.Products
group p by p.CategoryId into c
select new
{
categoryId = c.Key,
products = from PbyC in c
group PbyC by PbyC.Name into dupl
where dupl.Count() > 1
select dupl;
}
It works but LINQPad lists empty Categories (without any duplicated Product). How to modify the query?
It would be great to have Category name display somehow instead of Id.
EDIT: I have relationship between tables.
db.Products.GroupBy(p => new { p.Name, p.CategoryId })
.Select(g => new { g.Key.CategoryId, Count = g.Count })
.Where(ng => ng.Count > 1)
.Select(ng => ng.CategoryId);
That'll select the ids of the categories with more than one Product of the same name, and is assuming you don't have any relationships set up between the objects so you'll need to join the results to the Categories table to get detailed info.
If you do have a relationship set up then you can always change the CategoryId to Category (or whatever the related object is named) and just select Category.Name in the last Select.