6

これに対する答えはどこにも見つかりません。Reflector を使用して生成されたコードを調べ始める前に、次の質問をする価値があると思いました。

DataSet 内の DataTable に対して次の 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 はそれを使用しますか? つまり、値が「foo」である親テーブルの行を見つけてGetChildRows、子行を射影するために呼び出しますか?

または、これは明示的に指定する必要があるものですか? (もしそうなら、どうすればいいですか?)

4

3 に答える 3

3

Reflector を調べてみても、LINQ が DataRelations を使用しているという兆候は見られませんでしたが、そのコードは非常に読みにくいものでした。そこで、ちょっとしたパフォーマンス テストを作成しました。テストに見落としがない限り、結果は非常に明確です。いいえ、LINQ は結合された行を射影するために DataRelations と GetChildRows() を使用しません。子行を反復処理する場合は、LINQ クエリを作成して GetChildRows() を明示的に呼び出す必要があります。また、どちらのアプローチも、GetChildRows() によって返された配列を反復処理するコードを記述するほどパフォーマンスは高くありません。

残念なことに、大規模な DataSet でのパフォーマンスの違いは非常に大きいため、LINQ を明示的に実装されたコードに置き換える価値があることが多いため、LINQ には一般的に当てはまりません。

私のテストコードは以下です。結合を使用した LINQ 反復のタイミングは、DataRelation がその前または後に作成されたかどうかに関係なく、同じままです (私のマシンでは約 580 ~ 590 ミリ秒)。GetChildRows() を使用する LINQ 反復には約 280 ミリ秒かかります。

GetChildRows() を直接反復するのにかかる時間は 1 ミリ秒未満です。これは私にとってかなり驚くべきことです。最初にテストを実行したときに、コードにバグがあると思ったほどです。(そのため、毎回カウントを書き出しています。これは、コンパイラによってループが最適化されて存在しなくなっていないことを確認するためです。)

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);
            }
        }
    }
} 
于 2008-12-23T20:09:27.180 に答える
2

私はそうは思わない。この場合、LINQ to Objects はおそらく 2 つの側を通常の列挙可能なオブジェクトとして扱い、結合を手動で行います ( を見ずにDataRelation)。

于 2008-12-23T10:53:01.827 に答える
1

これは説明です: http://msdn.microsoft.com/en-us/library/bb386969.aspx

ただし、上記の例では、「JOIN」構文を使用した親子関係についてlinqに通知しています。可能であれば、強く型付けされたデータセットを作成することをお勧めします。そうすれば、すべての関係と結合が自動的に処理されます。

于 2008-12-23T02:33:43.193 に答える