Entity framework IQueryable 扩展方法不能用作子查询
Entity framework IQueryable extension methods do not work as a sub query
我喜欢尽可能使用扩展方法来编写查询。所以以下是对我有用的查询:
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 团队提出了这个问题,在调查之后他们想出了一个更优雅的解决方法。我已将其作为已接受的答案发布在下方。
您可以为您的 User
模型制作部分 class,其中包含静态 class:
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()
};
它不像扩展方法那么优雅,但我认为它应该可以解决问题...
我最终在 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 次数据库往返。将查询分解为多个语句实际上也提高了很多地方的可读性。
我喜欢尽可能使用扩展方法来编写查询。所以以下是对我有用的查询:
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 团队提出了这个问题,在调查之后他们想出了一个更优雅的解决方法。我已将其作为已接受的答案发布在下方。
您可以为您的 User
模型制作部分 class,其中包含静态 class:
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()
};
它不像扩展方法那么优雅,但我认为它应该可以解决问题...
我最终在 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 次数据库往返。将查询分解为多个语句实际上也提高了很多地方的可读性。