Desempenho em relação ao tipo de retorno para consulta LINQ Compatível com classificação automática
-
21-09-2019 - |
Pergunta
Isso não é realmente um problema, mas mais uma preocupação que eu apreciaria algumas informações, por favor.
WinForms C# .NET3.5 [SP1] Visual Studio 2008 usando LINQ2SQL (mais especificamente Plinqo... que é fantástico BTW!). Eu tenho um ResultSet retornando +/- 19000 linhas de dados (com cerca de 80 bytes de dados por linha) e optei por empurrar o método de recuperação de dados para o plano de fundo e atualizar a interface do usuário de acordo. Isso funciona bem.
No entanto, notei algumas diferenças de desempenho ao usar diferentes tipos de retorno para o meu método de recuperação de dados Ling. Eu sei que todo mundo sugere devolver um List<T>
ou IEnumarable<T>
, e defina o DataGridView DataSource para isso, mas infelizmente ele não suporta a classificação nativamente para objetos. Depois de alguns cavando, encontrei o SortableBindingList<T>
sobre Msdn aqui. Eu o apliquei, e a grade levou menos de um segundo para se preencher - no entanto, quando clico em uma coluna para classificá -la, demorou um pouco mais de um segundo para implementar o tipo.
Decidi então seguir a rota do Datatable, mas descobri que o método Todatatable foi removido, mas depois de mais escavação encontrou uma maneira de implementá -lo sobre isso Artigo do MSDN. Depois de aplicá -lo, descobri que a recuperação levou cerca de 2 segundos para preencher a grade, mas depois a classificação (em 19000 linhas!) Foi instantânea !! Naturalmente, fiquei com essa abordagem.
Lembre-se também de que desativei qualquer edição/adição/exclusão da grade. A grade é puramente para exibir dados. Quaisquer outras operações do CRUD são fornecidas pelos formulários de diálogo de acordo com a linha atual selecionada (coluna de chave primária oculta).
Aqui está o código que usei para os dois métodos:
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;
}
Agora eu sei que isso provavelmente parece ser um "perguntado e respondeu"Caso, mas eu realmente apreciaria sua contribuição, bem como quaisquer problemas conhecidos em ir para o CopyToDataTable()
rota.
Obrigado .... e desculpas pela consulta Looong!
Solução
Aqui está minha opinião sobre sua pergunta (indo para o SortableBindingList
rota).
Você usou um algoritmo genérico baseado em reflexão? Eu fiz isso no começo e a performance foi muito ruim. Finalmente, criei a seguinte solução: Forneça a classificação padrão em SortableBindingList<T>
mas também deixe em aberto a possibilidade de implementar classificação especializada em classes derivadas.
Aqui está algum código.
Dentro 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)
}
O genérico SortableBindingList<T>
Fornece um classificador básico baseado em reflexão:
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;
});
}
Como você pode ver, GetComparer()
é virtual, para que se possa substituí -lo em uma classe derivada de SortableBindingList<T>
Para fornecer um Muito de Comparador mais rápido, ajustado para o tipo de propriedade que está sendo classificada. Por exemplo, enquanto o comparador genérico classificou (por um String
Propriedade) 10000 Registros em 4 segundos, a comparadora especializada fez o mesmo trabalho em 70ms.
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;
}
}
Uma nota final: grande parte do código publicado foi copiado/inspirado de outras fontes, como So ou Microsoft, para que o crédito não seja meu. Minha única contribuição foi virtualizar o comparador, mas tenho certeza de que um pouco de Google surgiria melhor, soluções anteriores com base na mesma idéia.