为什么Linq To Sql绑定到GridView比直通SQL慢得多?

7

我比较了从数据库表中获取一些相当大的数据的两个查询。对于第一个查询,我使用了Linq To Sql,而对于另一个查询,则是通过ADO.NET使用了直通SQL。

我知道Linq To Sql在幕后要做很多工作,但它实际上在做什么呢?这两个查询获取了相同数量的数据,但Linq To Sql查询比另一个查询慢了5秒以上,并且使用了150mb以上的RAM!

这是我的测试代码:

使用Linq To Sql:

public void MakeList()
    {
        int start = Environment.TickCount;
        var document = from d in _dm.tDokuments select d;

        List<tDokument> documentList = document.ToList();
        int end = Environment.TickCount;

        GridView1.DataSource = documentList;
        GridView1.DataBind();

        Label1.Text = (end - start).ToString();
    }

透传SQL + ADO.NET:

public void MakeList()
    {

        int start = Environment.TickCount;
        SqlCommand sqlCommand = new SqlCommand("SELECT * FROM tDokument", _connection);
        SqlDataAdapter da = new SqlDataAdapter(sqlCommand);

        DataSet ds = new DataSet();
        da.Fill(ds);
        int end = Environment.TickCount;

        GridView1.DataSource = ds;
        GridView1.DataBind();

        Label1.Text = (end - start).ToString();
    }

2
当进程未附加到调试器时,您是否进行了测试? - Yannick Motton
我发现NHibernate存在类似的问题。我不确定“幕后”的大秘密是什么,但我猜ORM工具很慢... - Dani
1
@Dani - "ORM工具很慢"并不是从数据绑定性能测试中得出的结论。那是一个非常普遍(而且,在我看来,误导的)说法。 - TrueWill
5个回答

8
Linq2Sql返回强类型对象,而数据集则被填充了类似于哈希表的东西。
在Linq中,数据的填充和绑定到GridView使用了很多反射来生成所需的结果。
在第二段代码中,数据被加载到数据集并绑定到GridView。这实际上是使用哈希表加载数据并进行查找绑定。
哈希表操作总是比反射更快。对于少量数据,不会有明显的差异,但对于大量数据,您将会看到Linq中反射的影响。

谢谢您的回答。不过我还需要一个答案。什么是反射? - Poku
1
反射是以编程方式发现对象或类型的属性、方法和事件的过程。请查看MSDN上的System.Reflection命名空间。这就是Linq所要做的,将查询返回的值分配给tDokument对象的属性。与直接属性赋值相比,它速度较慢。 - Jason

4

你是否使用Profiler查看发送到SQL Server的实际SQL语句?

在这种情况下,我怀疑是客户端处理方式(DataSet vs List)导致了差异,但我并不是C#专家。


数据集与通用列表相比,将会有更多的开销与之相关,所以这真的不应该是问题所在。 - Jason

3
在您的Linq To Sql示例中,捕获并分析通过网络发送的SQL语句。使用SQL Profiler可以完成此操作。
直接在管理工作室中针对Example 1和2运行这些语句。可能不会看到查询计划上的任何实质性差异。
我认为大部分时间都花在构建C#对象上(Jason's answer nails it, 我认为)。

2
Linq to Sql需要从您的代码中找出要针对数据库运行的查询类型,然后将查询结果转换为强类型对象,这意味着会发生强制转换。而您的DataSet版本无法完成这两个步骤。
如果您对Linq to Sql的性能感兴趣,可以使用编译查询选项,在运行时消除解释的需要。具体信息请参考此处

1

您的测量可能不准确,请使用System.Diagnostics.StopWatch类进行时间测量:

static void Main(string[] args)
{
    var sw = Stopwatch.StartNew();
    Thread.Sleep(100);
    Console.WriteLine(sw.Elapsed.ToString());
    Console.Read();
}

明智的建议(尽管可能应该作为注释)。显然,Thread.Sleep可能会使线程睡眠超过100毫秒。 - RichardOD
是的,但当我运行上面的程序时,打印出来的结果比100ms略小。 - Jader Dias
不应该这样。在我的机器上,它总是比100毫秒长一点。 - RichardOD
00:00:00.0993450 或类似的内容连续出现3次。 - Jader Dias
@Jader- 非常有趣。我想我没有考虑到Windows计时器的不准确性。不过我找到了这篇文章,很有意思(是C++的,但我认为仍然相关)- http://www.codeproject.com/KB/system/sleepstudy.aspx - RichardOD
@Jader- 这里还有一篇有趣的帖子- https://dev59.com/QXM_5IYBdhLWcg3wmkiu。 - RichardOD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接