问题
I like to write my queries using extension methods where possible. So the following is a query which works for me:
int studentId =
(
from u in db.Users
.FromOrganisation(org.Id)
.IsStudent()
.IsActive()
where u.ExtId == dto.StudentExtId
select u.Id
).FirstOrDefault();
The extension methods are as follows:
public static IQueryable<User> IsStudent(this IQueryable<User> u)
{
return u.Where(x => x.Type == (int)UserTypes.Student);
}
However, when I use extension methods in a sub-query I get the following message:
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.
Here is the query which causes that message:
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();
What am I doing wrong?
Update: Alexander Derck posted a solution which worked well, but wasn't quite as nice as the original problem query. I raised it with the EF team, and after investigating they came up with a more elegant work-around. I have posted that below as the accepted answer.
回答1:
I eventually raised this with the Entity Framework team on GitHub. You can see the thread here, with a full description of why it happens:
https://github.com/aspnet/EntityFramework6/issues/98
It seems to have been raised as a suggestion for inclusion in EF 6.2, but until then, a very elegant work-around was suggested. You can read it in the thread, but I have copied it here for quick reference.
Here is the original query (where an error occurs due to an IQueryable extension method being used in a sub-query):
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();
And here is how to write it so that no error occurs:
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();
I can confirm that this style still only results in 1 round trip to the database. Breaking the query into multiple statements actually improves readability in a lot of places too.
回答2:
You can make a partial class for your User
model with a static class inside:
partial class User
{
public static class Q
{
public static Expression<Func<User,bool>> IsStudent
{
return x => x.Type == (int)UserTypes.Student;
}
}
}
Then your query would look like this:
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()
};
It's not as elegant as extension methods, but it should do the trick I think...
来源:https://stackoverflow.com/questions/34395488/entity-framework-iqueryable-extension-methods-do-not-work-as-a-sub-query