使用First()获取LINQ结果的第二项?

时间:2014-01-29 03:20:06

标签: c# linq entity-framework

我是Linq和EntityFramework的新手。这是我在学习时遇到的示例程序。

表格中的数据如下:

BlogId  Title
1       Hello Blog
2       New Blog
3       New Blog

我有以下Linq代码,尝试阅读第一个博客ID(预计为2):

var name = "New Blog";
var blogs = (from b in db.Blogs
                where b.Title == name
                orderby b.Title
                select b);//.ToList();

Console.Write("The first id: ");
Console.WriteLine(blogs.First().BlogId);

结果是3。

然后我使用 ToList()

var blogs = (from b in db.Blogs
                where b.Title == name
                orderby b.Title
                select b).ToList();
Console.Write("The first id: ");
Console.WriteLine(blogs.First().BlogId);

结果是2。

任何人都可以帮忙解释一下吗?或者这是一个错误吗?

感谢。

//////////////////////// 更新 /////////////// //////////////

我刚刚删除了数据库中的数据并插入了一些新项目。现在表格是这样的:

BlogId  Title
5       New Blog
6       New Blog
7       New Blog
8       New Blog

然后我运行上面的程序(不带ToList()),First()方法返回id 6 所以我假设该方法总是返回上面情况中的第二项。它似乎与RDBMS没有任何关系。谁能解释一下?

感谢。

/////////////////////////////////////////////// //////

仅供参考,以下是整个.cs文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Data.Entity;
using System.ComponentModel.DataAnnotations;

namespace SampleNew
{
    class Program
    {
        public class Blog
        {
            [Key]
            public Int32 BlogId { get; set; }
            public String Title { get; set; }
            public virtual List<Post> Posts { get; set; } 
        }

        public class Post
        {
            [Key]
            public Int32 PostId { get; set; }
            public String Title{ get; set; }

            public String Content { get; set; }
        }

        public class BlogContext : DbContext
        {
            public DbSet<Blog> Blogs{ get; set; }

            public DbSet<Post> Posts { get; set; }
        }

        static void Main(string[] args)
        {
            using (var db = new BlogContext())
            {
                // Create and save a new Blog 
                // Console.Write("Enter a name for a new Blog: ");
                var name = "New Blog";

                //var blog = new Blog { Title = name };
                var blogs = (from b in db.Blogs
                             where b.Title == name
                             orderby b.Title
                             select b).ToList();

                Console.Write("The first id: ");
                Console.WriteLine(blogs.First().BlogId);

                Console.WriteLine(blogs.Count());

                Blog blog = null;
                foreach (Blog b in blogs)
                {
                    blog = b;
                    Console.WriteLine(blog.BlogId);
                }
                Console.WriteLine(blog.BlogId);

                Console.WriteLine(blogs.First().BlogId);
                Console.WriteLine(blogs.First().BlogId);

                Console.WriteLine(blogs.Last().BlogId);
                Console.WriteLine(blogs.Last().BlogId);


                blog.Posts = new List<Post>();
                var post = new Post { Content = "Test Content2", Title = "Test Title2"};
                blog.Posts.Add(post);

                db.Posts.Add(post);
                db.SaveChanges();

                // Display all Blogs from the database 
                var query = from b in db.Blogs
                            orderby b.Title
                            select b;

                Console.WriteLine("All blogs in the database:");
                foreach (var item in query)
                {
                    Console.WriteLine(item.Title);
                }

                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            } 
        }
    }
}

2 个答案:

答案 0 :(得分:10)

你有两个相同的头衔,但ID不同。您的RDBMS可以灵活地按照其所希望的顺序返回与'New Blog'对应的行,因为您的代码没有指定超出标题要求的任何内容。此外,每次运行相同的查询时,甚至不需要以相同的顺序返回结果。

如果您希望获得可预测的结果,请在LINQ语句中添加“then by”以强制执行您希望的排序:

var query = from b in db.Blogs
    orderby b.Title, b.BlogId
    select b;

编辑:

  

当我运行上面的程序时,First()方法返回id 6,所以我假设该方法总是返回上面情况中的第二项。它似乎与RDBMS没有任何关系。谁能解释一下?

这也发生在RDBMS中,并且在没有LINQ的情况下可以重现。这是一个小型演示(link to sqlfiddle):

create table blogs(blogid int,title varchar(20));
insert into blogs(blogid,title) values (5,'New blog');
insert into blogs(blogid,title) values (6,'New blog');
insert into blogs(blogid,title) values (7,'New blog');
insert into blogs(blogid,title) values (8,'New blog');
SELECT * FROM Blogs ORDER BY Title

此查询以“自然”顺序生成结果:

BLOGID  TITLE
------  --------
5       New blog
6       New blog
7       New blog
8       New blog

然而,这个查询,就是EF运行以获取RDBMS中的First()项目

SELECT TOP 1 * FROM Blogs ORDER BY Title

以自然顺序返回第二个行:

BLOGID  TITLE
------  --------
6       New blog

这并不意味着它将在其他RDBMS(link to a demo with MySQL returning a different row for the same query)中返回同一行,甚至不会在同一个RDBMS中返回。它只是演示LINQ依赖于RDBMS来选择行,而RDBMS返回一个任意选择的行。

答案 1 :(得分:0)

我怀疑First()在没有ToList()的情况下进行了优化。

调用ToList()时,必须创建整个有序列表。因此,它将使用有效的排序算法对所有内容进行排序。

然而,使用First(),它只需要找到最小值。因此它可以使用更有效的算法,该算法基本上通过可枚举的一次并存储当前的最小对象值。 (因此它将导致最小值的第一个对象)。

这是一种不同的算法,然后对整个列表进行排序,从而获得不同的结果。

更新: 此外,这是一个数据库,它可能使用linq to sql,它将根据上面的描述生成一个不同的查询(获取一个排序列表vs获得第一个最小值)。