Entity framework IQueryable extension methods do not work as a sub query

[亡魂溺海] 提交于 2019-12-23 16:37:08

问题


I like to write my queries using extension methods where possible. So the following is a query which works for me:

int studentId = 
    (
        from u in db.Users
            .FromOrganisation(org.Id)
            .IsStudent()
            .IsActive()
        where u.ExtId == dto.StudentExtId
        select u.Id
    ).FirstOrDefault();

The extension methods are as follows:

public static IQueryable<User> IsStudent(this IQueryable<User> u)
{
    return u.Where(x => x.Type == (int)UserTypes.Student);
}

However, when I use extension methods in a sub-query I get the following message:

LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[eNotify.Domain.Models.User] IsActive(System.Linq.IQueryable`1[eNotify.Domain.Models.User])' method, and this method cannot be translated into a store expression.

Here is the query which causes that message:

var vm = from o in db.Organisations
         select new StaffStudentVm
         {
             StudentId = (
                 from u in db.Users
                     .FromOrganisation(org.Id)
                     .IsStudent()
                     .IsActive()
                 where u.ExtId == dto.StudentExtId
                 select u.Id
                 ).FirstOrDefault(),
             StaffId = (
                 from u in db.Users
                     .FromOrganisation(org.Id)
                     .IsStaff()
                     .IsActive()
                 where u.ExtId == dto.StaffExtId
                 select u.Id
                 ).FirstOrDefault()
         };

return vm.FirstOrDefault();

What am I doing wrong?

Update: Alexander Derck posted a solution which worked well, but wasn't quite as nice as the original problem query. I raised it with the EF team, and after investigating they came up with a more elegant work-around. I have posted that below as the accepted answer.


回答1:


I eventually raised this with the Entity Framework team on GitHub. You can see the thread here, with a full description of why it happens:

https://github.com/aspnet/EntityFramework6/issues/98

It seems to have been raised as a suggestion for inclusion in EF 6.2, but until then, a very elegant work-around was suggested. You can read it in the thread, but I have copied it here for quick reference.

Here is the original query (where an error occurs due to an IQueryable extension method being used in a sub-query):

var vm = from o in db.Organisations
         select new StaffStudentVm
         {
             StudentId = (
                 from u in db.Users
                     .FromOrganisation(org.Id)
                     .IsStudent()
                     .IsActive()
                 where u.ExtId == dto.StudentExtId
                 select u.Id
                 ).FirstOrDefault(),
             StaffId = (
                 from u in db.Users
                     .FromOrganisation(org.Id)
                     .IsStaff()
                     .IsActive()
                 where u.ExtId == dto.StaffExtId
                 select u.Id
                 ).FirstOrDefault()
         };

return vm.FirstOrDefault();

And here is how to write it so that no error occurs:

var stuList = db.Users.FromOrganisation(org.Id).IsStudent().IsActive();
var staffList = db.Users.FromOrganisation(org.Id).IsStaff().IsActive();

var vm = from o in db.Organisations
         select new StaffStudentVm
         {
             StudentId = (
                 from u in stuList
                 where u.ExtId == dto.StudentExtId
                 select u.Id
                 ).FirstOrDefault(),
             StaffId = (
                 from u in staffList
                 where u.ExtId == dto.StaffExtId
                 select u.Id
                 ).FirstOrDefault()
         };

return vm.FirstOrDefault();

I can confirm that this style still only results in 1 round trip to the database. Breaking the query into multiple statements actually improves readability in a lot of places too.




回答2:


You can make a partial class for your User model with a static class inside:

partial class User
{
    public static class Q
    {
        public static Expression<Func<User,bool>> IsStudent
        {
            return x => x.Type == (int)UserTypes.Student;
        }
    }
}

Then your query would look like this:

var vm = from o in db.Organisations
     select new StaffStudentVm
     {
         StudentId = (
             from u in db.Users
                 .FromOrganisation(org.Id)
                 .Where(User.Q.IsStudent)
                 .IsActive()
             where u.ExtId == dto.StudentExtId
             select u.Id
             ).FirstOrDefault(),
         StaffId = (
             from u in db.Users
                 .FromOrganisation(org.Id)
                 .IsStaff()
                 .IsActive()
             where u.ExtId == dto.StaffExtId
             select u.Id
             ).FirstOrDefault()
     };

It's not as elegant as extension methods, but it should do the trick I think...



来源:https://stackoverflow.com/questions/34395488/entity-framework-iqueryable-extension-methods-do-not-work-as-a-sub-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!