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

时间:2015-12-21 12:15:54

标签: 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();

扩展方法如下:

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

但是,当我在子查询中使用扩展方法时,我收到以下消息:

  

LINQ to Entities无法识别方法&#39; System.Linq.IQueryable`1 [eNotify.Domain.Models.User] IsActive(System.Linq.IQueryable`1 [eNotify.Domain.Models.User]) &#39;方法,并且此方法无法转换为商店表达式。

以下是导致该消息的查询:

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

我做错了什么?

更新 Alexander Derck发布了一个运行良好的解决方案,但并不像原始问题查询那样好。我和EF团队一起提出了这个问题,经过调查他们想出了一个更优雅的解决方案。我在下面发布了接受的答案。

2 个答案:

答案 0 :(得分:1)

我最终在GitHub上使用Entity Framework团队提出了这个问题。您可以在此处查看该主题,并详细说明其发生的原因:

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

似乎已经提出将其纳入EF 6.2,但在此之前,建议采用非常优雅的解决方案。你可以在线程中阅读它,但我已将其复制到此处以供快速参考。

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

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

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

答案 1 :(得分:0)

您可以使用静态类为User模型创建一个部分类:

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

然后您的查询将如下所示:

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

它不如扩展方法那么优雅,但它应该做我认为的技巧......