C# 实体框架IQueryable扩展方法不能作为子查询使用

C# 实体框架IQueryable扩展方法不能作为子查询使用,c#,entity-framework,linq,linq-to-sql,entity-framework-6,C#,Entity Framework,Linq,Linq To Sql,Entity Framework 6,我喜欢尽可能使用扩展方法编写查询。下面是一个对我有用的查询: int studentId = ( from u in db.Users .FromOrganisation(org.Id) .IsStudent() .IsActive() where u.ExtId == dto.StudentExtId select u.Id ).FirstOrDefault

我喜欢尽可能使用扩展方法编写查询。下面是一个对我有用的查询:

int studentId = 
    (
        from u in db.Users
            .FromOrganisation(org.Id)
            .IsStudent()
            .IsActive()
        where u.ExtId == dto.StudentExtId
        select u.Id
    ).FirstOrDefault();
扩展方法如下:

public static IQueryable<User> IsStudent(this IQueryable<User> u)
{
    return u.Where(x => x.Type == (int)UserTypes.Student);
}
我做错了什么

更新:
Alexander Derck发布了一个很好的解决方案,但没有原始问题查询那么好。我向EF团队提出了这个问题,在调查之后,他们提出了一个更优雅的解决方案。我已将此作为公认的答案发布在下面。

您可以为您的
用户
模型制作一个包含静态类的部分类:

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

它没有扩展方法那么优雅,但我认为它应该做到这一点……

我最终在GitHub上的实体框架团队中提出了这一点。您可以在此处看到该线程,并对其发生的原因进行完整描述:

它似乎是作为一项建议被纳入EF 6.2的,但在此之前,人们提出了一个非常优雅的解决方案。你可以在帖子里读到它,但我把它复制到这里以供快速参考

以下是原始查询(其中由于子查询中使用IQueryable扩展方法而发生错误):

下面是如何编写它以避免出现错误:

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();

我可以确认,这种样式仍然只会导致数据库的一次往返。将查询拆分为多个语句实际上也在很多地方提高了可读性。

您可以链接您的
IsActive()
方法吗?根据错误消息,您可能正在执行无法转换为sql的操作。这不是IsActive方法。如果我在查询中对它进行注释,它只会报告下一个扩展方法作为问题。也许你可以使用表达式而不是扩展方法?你有一个例子吗?我在一个答案中写了一个例子,如果扩展方法不可能解决这个问题,那么它应该会起作用。我把它忘得一干二净,直到今天又碰到了同样的问题。这个解决方案非常有效。非常感谢。
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()
     };
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();
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();