Производительность в отношении типа возврата для запроса LINQ, совместимого с автоматической сортировкой

на самом деле это не проблема, но больше беспокоит, и я был бы признателен за некоторые комментарии, пожалуйста.

Winforms C # .net3.5 [sp1] Visual Studio 2008 с использованием Linq2Sql (точнее, PLINQO ... что фантастически Кстати!). У меня есть набор результатов, возвращающий +/- 19000 строк данных (с примерно 80 байтами данных на строку), и я решил переместить метод извлечения данных в фоновый режим и соответствующим образом обновить пользовательский интерфейс. Это нормально работает.

Однако я заметил некоторые различия в производительности при использовании разных возвращаемых типов для моего метода извлечения данных Ling. Я знаю, что все предлагают вернуть List<T> или IEnumarable<T> и установить для этого источник данных DataGridView, но, к сожалению, он не поддерживает сортировку объектов изначально. Покопавшись, я нашел SortableBindingList<T> на MSDN здесь. Я применил его, и сетке потребовалось менее секунды, чтобы заполнить себя, однако, когда я щелкаю столбец, чтобы отсортировать его, для реализации сортировки потребовалось чуть больше секунды или около того.

Затем я решил пойти по маршруту DataTable, но обнаружил, что метод ToDataTable был удален, но после дополнительных поисков нашел способ реализовать его на этом статья MSDN. После его применения я обнаружил, что извлечение заняло около 2 секунд, чтобы заполнить сетку, но после этого сортировка (по 19000 строкам!) Была мгновенной !! Естественно, я придерживался этого подхода.

Также имейте в виду, что я отключил любое редактирование / добавление / удаление в сетке. Сетка предназначена исключительно для отображения данных. Любые другие операции CRUD предоставляются диалоговыми формами в соответствии с текущей выбранной строкой (столбец скрытого первичного ключа).

Вот код, который я использовал для обоих методов:

1) SortableBindingList

    //declare private member
    private SortableBindingList<PdtAllocation> listAlloc = null;

    private void RefreshData() {
        bcsDataContext ctx = new bcsDataContext();
        try {
            listAlloc = new SortableBindingList<PdtAllocation>(ctx.PdtAllocation.ToList());
        }
        catch (Exception) {
            throw;
        }
        finally {
            ctx.Dispose();
        }

        dataGridView1.DataSource = listAlloc;
    }

2) CopyToDatatable

    //declare private member
    private DataTable dt = null;

    private void RefreshData() {
        dt = new DataTable();
        bcsDataContext ctx = new bcsDataContext();
        try {
            ctx.PdtAllocation.CopyToDataTable(dt, LoadOption.PreserveChanges);
        }
        catch (Exception) {
            throw;
        }
        finally {
            ctx.Dispose();
        }

        dataGridView1.DataSource = dt;
    }

Теперь я знаю, что это, вероятно, похоже на случай «спросил и ответил», но я был бы очень признателен за ваш вклад, а также за любые известные проблемы с переходом по маршруту CopyToDataTable().

Спасибо .... и извиняюсь за долгий вопрос!


person Shalan    schedule 19.11.2009    source источник


Ответы (1)


вот мой ответ на ваш вопрос (иду по маршруту SortableBindingList).

Вы использовали общий алгоритм сортировки, основанный на отражении? Я сделал это сначала, и производительность была очень плохой. Наконец, я пришел к следующему решению: обеспечить сортировку по умолчанию в SortableBindingList<T>, но также оставить открытой возможность реализации специализированной сортировки в производных классах.

Вот код.

In SortableBindingList<T>.ApplySortCore():

protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
    // Check if the sorted property implements IComparable
    ...

    List<T> itemsList = (List<T>)this.Items;

    Comparison<T> comparer = GetComparer(prop);

    itemsList.Sort(comparer);

    if (direction == ListSortDirection.Descending)
    {
        itemsList.Reverse();
    }

    ...
    // Set sort state (is sorted, sort property, sort dir)
}

Общий SortableBindingList<T> предоставляет базовый сортировщик на основе отражения:

protected virtual Comparison<T> GetComparer(PropertyDescriptor prop)
{
    return new Comparison<T>(delegate(T x, T y)
    {
        if (prop.GetValue(x) != null)
            return ((IComparable)prop.GetValue(x)).CompareTo(prop.GetValue(y));
        else if (prop.GetValue(y) != null)
            return -1 * ((IComparable)prop.GetValue(y)).CompareTo(prop.GetValue(x));
        else
            return 0;
    });
}

Как видите, GetComparer() является виртуальным, поэтому его можно переопределить в классе, производном от SortableBindingList<T>, чтобы обеспечить гораздо более быстрый компаратор, настроенный в соответствии с типом сортируемого свойства. Например, в то время как общий компаратор отсортировал (по свойству String) 10000 записей за 4 секунды, специализированный компаратор проделал ту же работу за 70 мс.

class CustomerList : SortableBindingList<Customer>
{
    protected override Comparison<Customer> GetComparer(PropertyDescriptor prop)
    {
        Comparison<Customer> comparer = null;
        switch (prop.Name)
        {
            case "FirstName":
                comparer = new Comparison<Customer>(delegate(Customer x, Customer y)
                {
                    string xx = (null == x) ? null : x.FirstName;
                    string yy = (null == y) ? null : y.FirstName;
                    return String.Compare(xx, yy);
                });
                break;
            ...
        }
        return comparer;
    }
}

Последнее замечание: большая часть опубликованного кода была скопирована / вдохновлена ​​из других источников, таких как SO или Microsoft, так что это не моя заслуга. Единственным моим вкладом была виртуализация компаратора, но я уверен, что поиск в Google поможет лучше, более ранние решения, основанные на той же идее.

person Sorin Comanescu    schedule 19.11.2009
comment
Привет, Сорин! Вы использовали общий алгоритм сортировки, основанный на отражении? нет, просто использовали его как есть ... ничего не изменили. Код, предоставленный u, великолепен! но какая-то причина, по которой я НЕ должен использовать DataTable ?? - person Shalan; 19.11.2009
comment
Ну, моя первая реакция была: зачем копировать в DataTable, если можно отсортировать имеющуюся коллекцию? Я не знаю. Возможно, копирование в DataTable может дать другие удобные возможности, которые я сейчас не вижу. С нетерпением жду, что другие скажут по этому поводу. - person Sorin Comanescu; 19.11.2009
comment
Сам по себе ctx.PdtAllocation возвращает System.Data.Linq.Table ‹TEntity›. После этого остается выбор «конвертировать» в какой-либо другой тип. Насколько я понимаю, объем работы, выполняемой для .CopyToDataTable (), такой же, как и для .ToList () ... поэтому я выбрал вариант, который в итоге сработал лучше (на первый взгляд). Есть еще мысли по этому поводу? - person Shalan; 19.11.2009