我有一个方法 AddStudent(),它会查找具有相同名称的学生,如果有同名学生,则从数据库中返回现有学生,否则会创建一个新学生并将其添加到数据库中。
我很好奇为什么se = students.First<StudentEntity>();
在我尝试从LINQ查询中获取第一个结果时se = students.ElementAt<StudentEntity>(0);
失败时成功。这两种方法不一样吗?
该方法的完整代码如下所示。
public Student AddStudent(string name)
{
using (SchoolEntities db = new SchoolEntities())
{
// find student with same name via LINQ
var students = from s in db.StudentEntitySet
where s.name == name
select s;
StudentEntity se = default(StudentEntity);
// if student with the same name is already present, return
// that student
if (students.Count<StudentEntity>() > 0)
{
// if i use ElementAt, if fails with a "LINQ to Entities does not
// recognize the method 'StudentEntity ElementAt[StudentEntity]
// (System.Linq.IQueryable`1[StudentEntity], Int32)' method,
// and this method cannot be translated into a store expression.",
// but not when I use First. Why?
// se = students.ElementAt<StudentEntity>(0);
se = students.First<StudentEntity>();
}
else
{
// passing 0 for first parameter (id) since it's represented by
// a BigInt IDENTITY field in the database so any value
// doesn't matter.
se = StudentEntity.CreateStudentEntity(0, name);
db.AddToStudentEntitySet(se);
db.SaveChanges();
}
// create a Student object from the Entity object
return new Student(se);
}
}
谢谢!
答案 0 :(得分:8)
失败是因为ElementAt
方法是索引访问方法,而实体框架不知道如何将其转换为SQL。
使用First
方法时,Entity Framework可以将其转换为SQL查询中的TOP 1
子句。这非常简单。为了使用ElementAt
,它必须基于窗口函数(ROW_NUMBER()
)构建一个更复杂的查询,而且,它只是不够复杂,不能做到这一点。
它实际上是实体框架的documented limitation。不支持ElementAt
扩展名。
理论上,你可以这样写:
se = students.AsEnumerable().ElementAt<StudentEntity>(0);
这指示实体框架在AsEnumerable()
调用之后不要尝试“翻译”任何内容,因此它将检索所有结果(不仅仅是第一个)并迭代通过它们直到它到达你想要的元素(在这种情况下只是发生成为第一个)。
然而,与仅仅使用First()
相比,这会减慢操作很多,因为它不是仅从服务器获取1个结果,而是随后获取所有这些结果并过滤。我只会使用这种解决方法,如果由于一些奇怪的原因我需要获得第5或第10个元素或某些元素其他而不是第一个。