LINQ 是不是使用 DataRelations 来优化连接?

Posted

技术标签:

【中文标题】LINQ 是不是使用 DataRelations 来优化连接?【英文标题】:Does LINQ use DataRelations to optimize joins?LINQ 是否使用 DataRelations 来优化连接? 【发布时间】:2010-09-28 02:47:25 【问题描述】:

我在任何地方都找不到这个问题的答案,在我开始使用 Reflector 浏览生成的代码之前,我认为值得一问:

假设我对数据集中的数据表运行以下 LINQ 查询:

var list = 
   from pr in parentTable.AsEnumerable()
   join cr in childTable.AsEnumerable() on cr.Field<int>("ParentID") equals pr.Field<int>("ID")
   where pr.Field<string>("Value") == "foo"
   select cr;

如果父表和子表之间存在使用所示关键字段的 DataRelation,LINQ 会使用它吗?也就是说,它会在父表中找到Value为“foo”的行,然后调用GetChildRows来投影子行吗?

或者这是我必须明确指定的? (如果是这样,我该怎么做?)

【问题讨论】:

【参考方案1】:

在 Reflector 中四处挖掘似乎没有任何迹象表明 LINQ 使用 DataRelations,但该代码非常难以阅读。所以我写了一个小性能测试,除非我错过了一些关于测试的愚蠢的东西,否则结果是非常确定的:不,LINQ 不使用 DataRelations 和 GetChildRows() 来投影连接的行。如果要遍历子行,则必须制定 LINQ 查询以显式调用 GetChildRows()。而且这两种方法的性能都比不上编写迭代 GetChildRows() 返回的数组的代码。

有点不幸,因为大型数据集上的性能差异非常显着,以至于用显式实现的代码替换 LINQ 通常是值得的,而 LINQ 通常不是这样。

我的测试代码如下。使用连接的 LINQ 迭代的时间保持不变(在我的机器上大约 580-590 毫秒),无论 DataRelation 是在它之前还是之后创建的。使用 GetChildRows() 的 LINQ 迭代大约需要 280 毫秒,

直接在 GetChildRows() 上的迭代花费不到一毫秒。这对我来说非常令人惊讶 - 足以让我在第一次运行测试时假设我的代码中有一个错误。 (这就是为什么我每次都写出计数 - 以确保编译器没有优化循环。)

class Program

    static void Main(string[] args)
    
        Stopwatch sw = new Stopwatch();
        DataSet ds = new DataSet();
        DataTable t1 = new DataTable();
        t1.Columns.Add(new DataColumn
                           
                               ColumnName = "ID",
                               DataType = typeof (int),
                               AutoIncrement = true
                           );
        t1.PrimaryKey = new []  t1.Columns["ID"];
        ds.Tables.Add(t1);

        DataTable t2 = new DataTable();
        t2.Columns.Add(new DataColumn
        
            ColumnName = "ID",
            DataType = typeof(int),
            AutoIncrement = true
        );
        t2.Columns.Add("ParentID", typeof(int));
        t2.PrimaryKey = new[]  t2.Columns["ID"] ;
        ds.Tables.Add(t2);

        sw.Reset();
        sw.Start();
        PopulateTables(t1, t2);
        sw.Stop();
        Console.WriteLine("Populating tables took 0 ms.", sw.ElapsedMilliseconds);
        Console.WriteLine();

        var list1 = from r1 in t1.AsEnumerable()
                   join r2 in t2.AsEnumerable()
                       on r1.Field<int>("ID") equals r2.Field<int>("ParentID")
                   where r1.Field<int>("ID") == 1
                   select r2;

        sw.Reset();
        sw.Start();
        int count = 0;
        foreach (DataRow r in list1)
        
            count += r.Field<int>("ID");
        
        sw.Stop();
        Console.WriteLine("count = 0.", count);
        Console.WriteLine("Completed LINQ iteration in 0 ms.", sw.ElapsedMilliseconds);
        Console.WriteLine();

        sw.Reset();
        sw.Start();
        ds.Relations.Add(new DataRelation("FK_t2_t1", t1.Columns["ID"], t2.Columns["ParentID"]));
        sw.Stop();
        Console.WriteLine("Creating DataRelation took 0 ms.", sw.ElapsedMilliseconds);

        sw.Reset();
        sw.Start();
        var list2 =
            from r1 in t1.AsEnumerable()
            from r2 in r1.GetChildRows("FK_t2_t1")
            where r1.Field<int>("ID") == 1
            select r2;

        count = 0;
        foreach (DataRow r in list2)
        
            count += r.Field<int>("ID");
        
        sw.Stop();
        Console.WriteLine("count = 0.", count);
        Console.WriteLine("Completed LINQ iteration using nested query in 0 ms.", sw.ElapsedMilliseconds);
        Console.WriteLine();

        sw.Reset();
        sw.Start();
        DataRow parentRow = t1.Select("ID = 1")[0];
        count = 0;
        foreach (DataRow r in parentRow.GetChildRows("FK_t2_t1"))
        
            count += r.Field<int>("ID");
        
        sw.Stop();
        Console.WriteLine("count = 0.", count);
        Console.WriteLine("Completed explicit iteration of child rows in 0 ms.", sw.ElapsedMilliseconds);
        Console.WriteLine();

        Console.ReadLine();
    

    private static void PopulateTables(DataTable t1, DataTable t2)
    
        for (int count1 = 0; count1 < 1000; count1++)
        
            DataRow r1 = t1.NewRow();
            t1.Rows.Add(r1);
            for (int count2 = 0; count2 < 1000; count2++)
            
                DataRow r2 = t2.NewRow();
                r2["ParentID"] = r1["ID"];
                t2.Rows.Add(r2);
            
        
    
 

【讨论】:

好吧,您总是可以编写自己的 LINQ 实现 这样做 - 它并不像听起来那么工作(Jon 在 1 小时的演示中尝试了它,并取得了大部分进展)。 哦,这是一个危险的诱惑。【参考方案2】:

我不这么认为。在这种情况下,LINQ to Objects 可能只会将两侧视为常规可枚举对象,并手动进行连接(无需查看DataRelation)。

【讨论】:

【参考方案3】:

这是一个解释: http://msdn.microsoft.com/en-us/library/bb386969.aspx

但是在上面的示例中,您使用“JOIN”语法告诉 linq 父子关系。如果可能的话,我建议你制作一个强类型数据集,所有的关系和加入都会为你处理。

【讨论】:

以上是关于LINQ 是不是使用 DataRelations 来优化连接?的主要内容,如果未能解决你的问题,请参考以下文章

使用 XDocument 和 Linq 读取 XML - 检查元素是不是为 NULL?

使用 Linq 搜索值是不是存在于对象列表中

如何使用 System.Linq.Dynamic 检查 JSON 对象是不是与谓词匹配

使用 LINQ 检查列表是不是为空

如何使用 linq 检测一个集合字段是不是存在于另一个列表中?

为啥在 LINQ 中使用 First 而不是 FirstOrDefault? [复制]