Производительность в отношении типа возвращаемого значения для запроса LINQ, совместимого с автоматической сортировкой
-
21-09-2019 - |
Вопрос
На самом деле это не проблема, а скорее беспокойство, и я был бы признателен за ваше мнение, пожалуйста.
Winforms C# .net3.5[sp1] Visual Studio 2008 с использованием Linq2Sql (точнее ПЛИНКО...что, кстати, потрясающе!).У меня есть набор результатов, возвращающий +/- 19 000 строк данных (около 80 байт данных на строку), и я решил перенести метод получения данных на задний план и соответствующим образом обновить пользовательский интерфейс.Это работает нормально.
Однако я заметил некоторые различия в производительности при использовании разных типов возвращаемых данных для моего метода получения данных Ling.Я знаю, что все предлагают вернуть List<T>
или IEnumarable<T>
, и установите для него источник данных DataGridView, но, к сожалению, он не поддерживает собственную сортировку объектов.Немного покопавшись, я нашел SortableBindingList<T>
на MSDN здесь.Я применил его, и сетка заполнилась менее чем за секунду, однако, когда я нажимаю на столбец, чтобы отсортировать его, реализация сортировки заняла чуть больше секунды или около того.
Затем я решил пойти по маршруту DataTable, но обнаружил, что метод ToDataTable был удален, но после дальнейшего копания нашел способ реализовать его в этом случае. Статья MSDN.Применив его, я обнаружил, что заполнение сетки занимает около 2 секунд, но после этого сортировка (по 19 000 строк!) происходит мгновенно!Естественно, я придерживался этого подхода.
Также имейте в виду, что я отключил любое редактирование/добавление/удаление в сетке.Сетка предназначена исключительно для отображения данных.Любые другие операции CRUD предоставляются диалоговыми формами в соответствии с текущей выбранной строкой (столбец скрытого первичного ключа).
Вот код, который я использовал для обоих методов:
1) Сортируемый список привязок
//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) Копироватьтаблицу данных
//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()
маршрут.
Спасибо....и извините за долгий вопрос!
Решение
вот мой взгляд на ваш вопрос (собираясь SortableBindingList
маршрут).
Использовали ли вы общий алгоритм сортировки на основе отражения?Сначала я сделал это, и производительность была очень плохой.Наконец я придумал следующее решение:обеспечить сортировку по умолчанию SortableBindingList<T>
но также оставить открытой возможность реализации специализированной сортировки в производных классах.
Вот некоторый код.
В 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 поможет найти более ранние решения, основанные на той же идее.