c# - Entity Framework IQueryable 扩展方法不能用作子查询

标签 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 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.

这是导致该消息的查询:

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 团队提出了这个问题,在调查之后他们想出了一个更优雅的解决方法。我已将其作为已接受的答案发布在下方。

最佳答案

我最终在 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 次数据库往返。将查询分解为多个语句实际上也提高了很多地方的可读性。

关于c# - Entity Framework IQueryable 扩展方法不能用作子查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34395488/

相关文章:

c# - 如何为我的代码正确使用 "using"语句

entity-framework - 我可以使用 AutoMapper 将一个列表映射到另一已实例化对象的列表吗?

linq - 使用 LINQ to SQL : Converting byte array or stream to Binary 存储图像

c# - 此代码中两个选择器函数的返回值之间的差异

c# - LINQ to XML - 检查是否为空?

java - 队列上 IBM MQ 的 IsOpen 和 OpenStatus 属性有何区别?

c# - 数据绑定(bind)到中继器

c# - 在 C# WinForm 中单击子 ToolStripMenuItem 时,ToolStripMenuItem 不关闭

c# - 如何使用 linq 连接 2 个表但避免匿名对象

c# - MySql + EntityFramework "Backmodel have changed"