Using lookup tables with Entity Framework - c#

I've had to use a lookup table in an EF project and it took me a bit of tinkering to get it working but I'm unsure how to query something.
If I have three tables (this is a test project to illustrate the question):
Person
------
ID - Int - PK
PersonName - varchar[50]
and
Skill
-----
ID - Int - PK
SkillName - varchar[50]
which are linked by a lookup table:
PS_Lookup
---------
ID - Int - PK
PersonID - Int - FK : Person.ID
SkillID - Int - FK : Skill.ID
Now, if I want to return all users who have a skill of ID 1 I've worked out I can do:
var result = (from p in context.People
select new
{
PersonID = p.ID,
PersonName = p.PersonName,
FirstSkill = (from s in p.PS_Lookup
where s.ID == 1
select s.SkillName),
}).ToList();
My question is, what do I need to change on the above query just to return the PersonName and PersonID of everyone who has the skill with the ID 1?
i.e. Not returning "FirstSkill". I don't need the name as I know what FirstSkill is and I can't see what I have to do with the where clause.

You need to use where condition in your query:
var result = (from p in context.People
where p.PS_Lookup.Any(ps => ps.ID == 1)
select new { p.ID, p.PersonName })
.ToList();

Related

Select items with the same foreign key

I have a question regarding Linq in C#.
Let's say that I have 3 tables "Company", "Employee" and "Job".
Company
--------
Id - int (PK)
Name - string
Employee
--------
Id - int (PK)
CompanyId - int (FK on Company.Id)
Name - string
Job
--------
Id - int (PK)
CompanyId - int (FK on Company.Id)
EmployeeId - int (FK on Employee.Id)
Name - string
Something like that:
enter image description here
The important thing is that every work must be connected to a company but not necessarily to an employee. However, each employee must be connected to a company. For example we can have racord like that:
Company
--------
Id Name
1 'A'
2 'B'
3 'C'
Employee
--------
Id CompanyId Name
1 1 'A'
2 1 'B'
3 2 'C'
Job
--------
Id CompanyId EmployeeId Name
1 1 1 'clean'
2 1 2 'wash'
3 2 2 'buy'
4 3 NULL 'sell'
And now with linq I would like to get all jobs that are assigned to this employee and other employees from the same company.
So in this case it will should are jobs with id 1 and 2 becouse employee 1 is assigned to company 1 and job with id 2 also has assigned to this company. How could I achieve this using linq but something like this:
_context.Job.Where (x => x)
It is important to make only one query to the database.
Tkanks.
You can simplify the problem by thinking of it in two steps: find the company for a given employee, find all the jobs for that company:
var companyToFind = from e in Employee
where e.Id == empToFind
select e.CompanyId;
var jobsToFind = from j in Job
where companyToFind.Any(cid => cid == j.CompanyId)
select j;
In LINQ to SQL, EF Core 2.2 and EF Core 3.x the Any is translated to an EXISTS query in SQL.
Note since you know there should be only one answer to companyToFind, sending two queries to the database may be more efficient.
For LINQ to SQL or EF Core 3.x, you could also nest the first query and reduce to a single result:
var jobsToFind = from j in Job
where (from e in Employee
where e.Id == empToFind
select e.CompanyId).First() == j.CompanyId
select j;

EF Core 3.0 - Convert SQL to LINQ

The example given in the blog has the following
from e in s.StudentCourseEnrollments where courseIDs.Contains(e.Course.CourseID) select e
The contains logic will not work when we are looking for an exact match. If a student has enrolled for 6 courses (ex : 1,2,3,4,5,6) and the requested list contains 5 (ex: 1,2,3,4,5) the query will return a match when it should not. The other way works well when the student has enrolled in a subset of the requested list.
Below solution works but need help to convert the below sql to LINQ (EF Core 3.0) ?
Create TABLE dbo.Enrollments (StudentId INT NOT NULL, CourseId INT NOT NULL)
insert into dbo.Enrollments values (1,1)
insert into dbo.Enrollments values (1,2)
insert into dbo.Enrollments values (1,3)
insert into dbo.Enrollments values (1,4)
insert into dbo.Enrollments values (1,5)
insert into dbo.Enrollments values (1,6)
DECLARE #TempCourses TABLE
(
CourseId INT
);
INSERT INTO #TempCourses (CourseId) VALUES (1), (2), (3),(4),(5);
SELECT t.StudentId
FROM
(
SELECT StudentId, cnt=COUNT(*)
FROM dbo.Enrollments
GROUP BY StudentId
) kc
INNER JOIN
(
SELECT cnt=COUNT(*)
FROM #TempCourses
) nc ON nc.cnt = kc.cnt
JOIN dbo.Enrollments t ON t.StudentId = kc.StudentId
JOIN #TempCourses n ON n.CourseId = t.CourseId
GROUP BY t.StudentId
HAVING COUNT(*) = MIN(nc.cnt);
drop table dbo.Enrollments
db<>Fiddle
I don't know about the SQL query, but the EF Core 3.0 LINQ query for the same task is something like this:
var matchIds = new[] { 1, 2, 3, 4, 5 }.AsEnumerable();
var query = dbContext.Students
.Where(s => s.Enrollments.All(e => matchIds.Contains(e.CourseId))
&& s.Enrollments.Count() == matchIds.Count());
The main matching job is done with All subquery. Unfortunately that's not enough for the case when related link records are more than the matching ids, so additional counts comparison solves that.
You can achieve it with a simple way like this, live demo here
Let's say that you've got the list of enrollments by this way
var enrollments = from s in dc.Students
from c in s.Courses
select new { StudentID = s.StudentID, CourseID = c.CourseID };
Then get the result by this way
var groupedEnrollment = enrollments.GroupBy(p => p.StudentId)
.Select(g => new
{
StudentId = g.Key,
Courses = g.Select(p => p.CourseId).ToArray()
});
var result = groupedEnrollment.Where(g =>
g.Courses.Length == courses.Length &&
g.Courses.Intersect(courses).Count() == courses.Length);

Trouble generating c# linq to entities (EF) query for a 3 table join with or and one table with no key relationships

Been trying a bunch of things but can't get anything to work for entity framework. Table structure:
Device DeviceLegacy BillingDeviceProduct
------ ------------ --------------------
ID DeviceID SKU = 123
Num LegacyID NID
Name LegacyUID SID
(extraneous fields omitted)
DeviceLegacy.DeviceID is a foreign key relationship to Device.ID.
DeviceLegacy and BillingDeviceProduct have no relationships in the database but if DeviceLegacy has a BillingDeviceProduct (of SKU=123) either LegacyID will equal NID or LegacyUID will equal SID. Sometimes both values will match. Sometimes they won't, but 1 of those will always match.
SQL:
select Num, Name from BillingDeviceProduct bdp
join DeviceLegacy leg on leg.LegacyUID = bdp.SID or leg.LegacyID = bdp.NID
join Device on Device.id = leg.DeviceID
where SKU = 123
We have the following but that doesn't handle the condition when both values don't match between DeviceLegacy and BillingDeviceProduct:
from device in Devices
join dlegacy in DeviceLegacies
on device.ID equals dlegacy.DeviceID
into dl
from devicelegacy in dl
join bdproduct in BillingDeviceProducts
on new { devicelegacy.LegacyID, devicelegacy.LegacyUID } equals new { LegacyID = bdproduct.NID, LegacyUID = bdproduct.SID }
into bdp
from deviceproduct in bdp
where deviceproduct.SKU == 123
select new { device.Num, device.Name}
seems like you could just use the foreign key relationship to exclude one join atleast
from bdp in BillingDeviceProducts
where bdp.SKU = 123
from dl in DeviceLegacies.Include("Device")
where (bdp.SID == dl.LegacyUID || dbp.NID == dl.LegacyID)
select new {
Num = dl.Device.Num,
Name = dl.Device.Name
}

C# Linq finding value

I want to return the depart number that is not found Employee Table by comparing Department table.
Person Table
ID name salary job commision DeptID
--------------------------------------------------------------
P001 Jon 2000 Manager NULL 1
P002 Skeet 1000 Salesman 2000 1
P003 James 2340 Developer NULL 2
P004 greed 4500 Developer NULL 2
P005 Joel 1330 Salesman 1200 1
P006 Deol 5000 Architect NULL 2
Department Table
DeptID DeptName
1 Management
2 Software
3 ERP
SQL
select DeptId from dept
where deptId not in (select deptid from person)
When i try to execute the below code
LINQ
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID };
I receive the following error
Operator '!=' cannot be applied to operands of type 'int?' and 'System.Linq.IQueryable'
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId).FirstOrDefault()
select new { DeptID = n.DeptID };
You are trying to compare DeptID with a collection 1 or more department Ids. Even if there would only logically be one result for a DeptID, syntactically you need to specify that you want the first hit.
Suggested rephrasing:
var q = from m in context.Depts
where
!context.Persons.Select(p => p.DeptID).Contains(m.DeptID)
select new { DeptID = m.DeptID };
It sounds that your DeptID field in SQL is set to allow nulls. In that case you'd probably want something along the lines of this:
var qry = from n in context.Persons
where n.DeptID.Value !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID.Value };
I think it should be something like that. I tried to get a list of DeptID's first and then implement a NOT IN with contains :
var deptIDs = context.Persons
.Where( p => !context.Depts
.Select(d => new {DeptID = d.DeptID})
.Contains( p.DeptID )
)
.Select( p => new { DeptID = n.DeptID } );

compare two list in linq

i'm having to two tables, which i need to compare both the table.
Let say table 1) Student profile 2) staff list.-- in this each staff has their student id , like many row
I need to get the current staff who log in's student id which may be many row.
And the resulted student profile from table 1.
Based on what you're describing the Staff table has multiple entries (at least 1) for each staff member, and those entries have a unique StudentID mapping staff to student. Something like this:
StaffID = 1, StudentID = 3
StaffID = 1, StudentID = 21
StaffID = 2, StudentID = 45
...
With the above type of setup, you can grab the list of students that belong to the currently signed in staff user, then query the student table for matching students:
int staffID = 1; // current staff user
var staffStudents = StaffTable.Where(s => s.StaffID == staffID)
.Select(s => s.StudentID);
var query = StudentTable.Where(student =>
staffStudents.Any(id => id == student.StudentID));

Categories

Resources