Использует ли LINQ DataRelations для оптимизации соединений? - PullRequest
6 голосов
/ 23 декабря 2008

Я нигде не могу найти ответ на этот вопрос, и перед тем, как приступить к копированию сгенерированного кода с помощью Reflector, я подумал, что стоит спросить:

Предположим, у меня есть следующий запрос LINQ для DataTables в DataSet:

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 для проецирования дочерних строк?

Или это то, что я должен указать явно? (И если да, то как мне это сделать?)

Ответы [ 3 ]

3 голосов
/ 23 декабря 2008

Копание в Reflector, похоже, не указывало на то, что LINQ использует DataRelations, но этот код ужасно трудно читать. Поэтому я написал небольшой тест производительности, и, если в тесте есть что-то глупое, что я пропустил, результаты довольно точные: нет, LINQ не использует DataRelations и GetChildRows () для проекции объединенных строк. Если вы хотите перебрать дочерние строки, вам нужно сформулировать запрос LINQ для явного вызова GetChildRows (). И ни один из подходов не является столь же эффективным, как написание кода, который перебирает массив, возвращаемый GetChildRows ().

Довольно печально, потому что разница в производительности на больших DataSets достаточно значительна, так что замена LINQ явно реализованным кодом часто имеет смысл, что, как правило, не относится к LINQ.

Мой тестовый код ниже. Время итерации LINQ с использованием объединения остается неизменным (около 580-590 мс на моем компьютере) независимо от того, создан ли DataRelation до или после него. Итерация LINQ, использующая GetChildRows (), занимает около 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);
            }
        }
    }
} 
2 голосов
/ 23 декабря 2008

Я так не думаю. В этом случае LINQ to Objects, вероятно, просто будет обрабатывать две стороны как обычные перечисляемые объекты и выполнит соединение вручную (не глядя на DataRelation).

1 голос
/ 23 декабря 2008

Это объяснение: http://msdn.microsoft.com/en-us/library/bb386969.aspx

Но в приведенном выше примере вы рассказываете linq о родительско-дочерних отношениях с синтаксисом «JOIN». Я бы порекомендовал вам сделать набор данных со строгой типизацией, если это возможно, и все отношения и соединения будут обработаны для вас.

...