Question

Assuming a pure many to many where the primary key is the composite of two foreign keys. For example "Employee Privileges" in Northwind has EmployeeID and PrivilegeID. This m2m obviously relates the Employees table to the Privilege table.

I need to write a query with EF that returns all Employees that are NOT associated with a specified PrivilegeID, regardless of whether or not it is related to other PrivilegeIDs.

I have an EF query that works for Sql Server using Except() but it doesn't work for Oracle.

If I were writing the query in t-sql, I would write a query that filters Employee Privileges with the required Privileges "see EmpPrivQuery below". I would then RIGHT join EmpPrivQuery to Employee as seen in EmployeesMissingPrivileges to complete the filter.

We currently have Employee and Privilege models with relationship properties to create the many to many table. The project uses EF code first to create the schema.

Can this be done with EF join syntax? Can it be done without creating an Entity model for the EmployeePrivilege table?

EmpPrivQuery:
SELECT [Employee Privileges].[Employee ID], [Employee Privileges].[Privilege ID] 
FROM [Privileges] INNER JOIN [Employee Privileges] ON Privileges.[Privilege ID] = [Employee Privileges].[Privilege ID]
WHERE (((Privileges.[Privilege Name])="P3"));


EmployeesMissingPrivileges:
SELECT EmpPrivQuery.[Employee ID], Employees.*
FROM Employees LEFT JOIN EmpPrivQuery ON Employees.ID = EmpPrivQuery.[Employee ID]
WHERE (((EmpPrivQuery.[Employee ID]) Is Null));

This block was in original post but clouds the question. Retained for comment context. I'm using Devart dotConnect as as the Oracle data provider. The error that Oracle is throwing is ORA-00904: "Extent1"."EmployeeID": invalid identifier. In adapting the code base that was written for sql server to work against oracle, this is a common error that I've needed to resolve. In most cases I have been able to resolve it by re-writing the query to move relationships to other tables out of the WHERE predicate (where it is very easy to be dynamic) up into the main body of the query using SelectMany(). This tends to flatten the query being sent to the database server and Oracle appears to require that.

Was it helpful?

Solution

You can use EF to create basically the same queries posted in the question. I started by creating a poco model EmployeePrivilege with properties: int PrivilegeID & int EmployeeID. I did not add this to the DbContext.

var EmpPrivQuery = ctx.Privileges
                       .Where(p => p.PrivilegeName == "P3")
                       .SelectMany(p => p.Employees, (p, e) => new EmployeePrivilege{EmployeeID = e.EmployeeID, PrivilegeID = p.PrivilegeID}
                       .Distinct();

var employeesMissingPrivilege = from e in Employees
                                join epq in EmpPrivQuery
                                on e.EmployeeID equals epq.EmployeeID
                                into jointable
                                where jointable.Count()==0
                                select e;

I just realized you can also get the same result without creating the poco EmployeePrivilege as follows:

var EmpPrivQuery = ctx.Privileges
                        .Where(p => p.PrivilegeName == "P3")
                        .SelectMany(p => p.Employees.Select(e => e.EmployeeID)
                        .Distinct();

var employeesMissingPrivilege = from e in Employees
                                join epq in EmpPrivQuery
                                on e.EmployeeID equals epq
                                into jointable
                                where jointable.Count()==0
                                select e;

Both of these EF queries return Employees missing specified privileges against both Sql Server and Oracle (using Devart's dotConnect for Oracle).

Many posts that I read referred to using DefaultIfEmpty() to achieve a left outer join. The queries above work, however, please post if there is a better way to this result using DefaultIfEmpty().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top