从LINQ查询中获取第一个结果 - 当First <t>()成功时,为什么ElementAt <t>(0)失败?</t> </t>

时间:2010-04-16 02:13:46

标签: c# linq entity-framework

我有一个方法 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);
    }
}

谢谢!

1 个答案:

答案 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个元素或某些元素其他而不是第一个。