简体   繁体   中英

Passing delegate to linq query in EF Core

I have the following delegate declaration:

private Func<Employee, bool> _exclude;

Then elsewhere in my code I set its value to this:

_exclude = (subject) =>
  !subject.IsDeleted && subject.Location.Department.Company.GroupId.Equals(_groupId);

The objective is to reuse the filter across all queries. This works fine if I'm going to materialize an employee instance like this:

Employee theEmployee = db.Employees
    .Include(e=>e.Location)
    .ThenInclude(e => e.Department)
    .ThenInclude(e => e.Company)
    .Where(e => e.EmployeeId == EmployeeId && _exclude(e))
    .FirstOrDefault();

But it fails when I just wanted to retrieve a single value,like for instance, EmployeeId:

string employeeId = db.Employees
    .Include(e=>e.Location)
    .ThenInclude(e => e.Department)
    .ThenInclude(e => e.Company)
    .Where(e => e.EmployeeId == EmployeeId && _exclude(e))
    .Select(e => e.EmployeeId)
    .FirstOrDefault();

The above fails producing a NullReferenceException in the Func delegate _exclude because subject.Location value is null, that means the employee passed to the delegate is not full materialized as per Includes.

What can be the cause that it successfully materialize the Employee graph when a full fledged Employee is needed but fails for the projection query, or how should the query be composed in this cases?

I am using EF Core

What can be the cause that it successfully materialize the Employee graph when a full fledged Employee is needed but fails for the projection query

In both cases the _exclude(e) is not translated to SQL, but evaluated in memory, and it fails in the second scenario because of the Ignored includes and the lack of lazy loading support.

It's always better to use Expression<Func<...>> when possible because they are translated to SQL and evaluated at database side, so includes don't matter.

In your case it's quite easy to change the type of _exclude variable (the lambda syntax of assigning it remains the same) and use chained Where instead on && :

private Expression<Func<Employee, bool>> _exclude;

(based on the filter semantics, it should really be called _include or _filter , but anyway)

and

_exclude = (subject) =>
  !subject.IsDeleted && subject.Location.Department.Company.GroupId.Equals(_groupId);

Now this works:

Employee theEmployee = db.Employees
    .Include(e=>e.Location)
    .ThenInclude(e => e.Department)
    .ThenInclude(e => e.Company)
    .Where(e => e.EmployeeId == EmployeeId)
    .Where(_exclude)
    .FirstOrDefault();

as well as this:

string employeeId = db.Employees
    // not needed, but will not hurt if used, will be ignored anyway
    //.Include(e=> e.Location)
    //.ThenInclude(e => e.Department)
    //.ThenInclude(e => e.Company)
    .Where(e => e.EmployeeId == EmployeeId)
    .Where(_exclude)
    .Select(e => e.EmployeeId)
    .FirstOrDefault();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM