实体框架IQueryable扩展方法不能用作子查询
本文关键字:查询 不能 方法 框架 IQueryable 扩展 实体 | 更新日期: 2023-09-27 18:27:31
我喜欢尽可能使用扩展方法编写查询。因此,以下是一个对我有效的查询:
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无法识别方法"System.LINQ.IQueryable"1[eNotify.Domain.Models.User]IsActive(System.LINQ[IQueryable`1[eNitify.Domain.Models.User])",并且此方法无法转换为存储表达式。
以下是导致该消息的查询:
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发布了一个运行良好的解决方案,但不如最初的问题查询那么好。我向英孚团队提出了这个问题,经过调查,他们想出了一个更优雅的解决方案。我已将其作为已接受的答案发布在下面。
我最终在GitHub上向实体框架团队提出了这个问题。你可以在这里看到这个线程,并对它发生的原因进行了完整的描述:
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();
我可以确认,这种风格仍然只会导致一次数据库往返。将查询分解为多个语句实际上在很多地方也提高了可读性。
您可以为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()
};
它没有扩展方法那么优雅,但我认为它应该做到。。。