maneira mais rápida para converter tabela de dados de lista genérica
-
22-07-2019 - |
Pergunta
Eu tenho um método de seleção camada de dados que retorna uma tabela de dados. Chama-se a partir de um método de camada de negócios que deve então retornar uma lista genérica fortemente tipado.
O que eu quero fazer é muito semelhante (mas não o mesmo que) esta pergunta:
Como você converter um DataTable em uma lista genérica / a>
O que é diferente é que eu quero a lista para conter objetos fortemente tipados em vez de DataRows (também, eu não tenho linq disponíveis aqui ainda).
Estou preocupado com o desempenho. O método de camada de negócios, por sua vez ser chamado a partir da camada de apresentação, e os resultados vão ser repetido para exibição para o usuário. Parece muito desperdício para adicionar uma iteração extra na camada de negócios, apenas a fazê-lo novamente de imediato para a apresentação, então eu quero que este seja o mais rápido possível.
Esta é uma tarefa comum, então eu realmente estou procurando um bom padrão que pode ser repetido várias vezes.
Solução
Você conhece a estrutura do DataTable
eo objeto digitado antes do tempo? Você poderia usar um delegado para fazer o mapeamento. Se não o fizer (ou seja, tudo que você sabe é um Type
e propriedades), existem formas de acelerar o acesso de membros dinâmica (como HyperDescriptor
).
De qualquer forma, considere um bloco de iterator; Dessa forma, você não tem para tamponar a objetos toda uma segunda vez; Claro, se você está lidando apenas com rowcounts smallish isso não é um problema.
Você pode esclarecer qualquer um desses pontos? Posso adicionar um detalhe muito mais ...
No mais simples, o que está errado com:
DataTable table = new DataTable {
Columns = {
{"Foo", typeof(int)},
{"Bar", typeof(string)}
}
};
for (int i = 0; i < 5000; i++) {
table.Rows.Add(i, "Row " + i);
}
List<MyType> data = new List<MyType>(table.Rows.Count);
foreach (DataRow row in table.Rows) {
data.Add(new MyType((int)row[0], (string)row[1]));
}
(os problemas no acima pode orientar o caminho certo ...)
Outras dicas
O problema com o exemplo acima é que é terrivelmente lento. Eu tenho um DataTable com cerca de 400 linhas e esta conversão leva uma boa 5 ou 6 segundos!
Ele faz parecer uma tarefa comum muito, por isso estou surpreso de não ver alguém aqui com uma solução de maior performance.
* Atualização !! * Apenas por diversão, eu pensei que eu tentaria converter usando LINQ em vez de iteração através do DataTable e adicionar a minha lista. O seguinte é o que eu fiz:
List<MyObject> returnList = new List<MyObject>();
MyDataTable dtMyData = new MyTableAdapter().GetMyData();
returnLists = (from l in dtMyData
select new MyObject
{
Active = l.IsActive,
Email = l.Email,
//...
//About 40 more properties
//...
ZipCode = l.Zipcode
}).ToList();
O primeiro método (iteração através de cada linha) levou 5,3 segundos eo método usando LINQ levou 1,8 segundos!
Você precisará percorrer os DataRows e convertê-los em seus objetos em algum momento de qualquer maneira, mas você poderia escrever uma coleção personalizada com um construtor que leva uma coleção de DataRows e que converte cada item em seu tipo de objeto, quando solicitado, em vez de uma só vez.
Say você define sua coleção personalizada que implementa IList<T>
. Ele poderia, então, ter dois campos internos - uma referência à coleção de DataRows e uma List<T>
. O List<T>
iria ser inicializado para o comprimento da coleção de DataRows mas com valores nulos.
Agora, no indexador você pode verificar se o List<T>
contém um valor nesse índice e se não é possível criar o objeto usando qualquer meio seria útil e armazená-lo lá antes de devolvê-lo.
Este seria adiar a criação de seus objetos até que eles foram solicitados e você só iria criar os objetos que foram solicitados.
Seus objetos provavelmente precisa de um construtor tomar um DataRow ou você iria precisar de algum tipo de fábrica para criá-los, mas isso é outro assunto.
Apenas para proporcionar mais algum usuário - simpatia para a resposta de Mark em um aplicativo de console simples:
class Program
{
static void Main(string[] args)
{
//define a DataTable obj
DataTable table = new DataTable
{
Columns = {
{"Foo", typeof(int)},
{"Bar", typeof(string)}
}
};
//populate it the DataTable
for (int i = 0; i < 3; i++)
{
table.Rows.Add(i, "Row " + i);
}
List<MyType> listWithTypedObjects= new List<MyType>(table.Rows.Count);
foreach (DataRow row in table.Rows)
{
listWithTypedObjects.Add(new MyType((int)row[0], (string)row[1]));
}
Console.WriteLine(" PRINTING THE POPULATED LIST ");
foreach (MyType objMyType in listWithTypedObjects)
{
Console.Write(" I have object of the type " + objMyType.ToString() + " => " );
Console.Write(" with Prop1OfTypeInt " + objMyType.Prop1OfTypeInt.ToString() + " , ");
Console.WriteLine(" with Prop1OfTypeInt " + objMyType.Prop2OfTypeString.ToString() + " ");
}
Console.WriteLine(" \n \n \n HIT A KEY TO EXIT THE PROGRAM ");
Console.ReadKey();
}
}
class MyType {
public int Prop1OfTypeInt { get; set; }
public string Prop2OfTypeString { get; set; }
/// <summary>
/// Note the order of the passed parameters is important !!!
/// </summary>
public MyType( int prop1OfTypeInt , string prop2OfTypeString)
{
this.Prop1OfTypeInt = prop1OfTypeInt;
this.Prop2OfTypeString = prop2OfTypeString;
}
}
public partial class issuereceive_manageroffice_bal
{
public int issue_id{get;set;}
public string process{get;set;}
public DateTime issue_date{get;set;}
public TimeSpan issue_time{get;set;}
public string eg_no{get;set;}
public string lotno{get;set;}
public string clarity{get;set;}
public string sieves{get;set;}
public string shape{get;set;}
public double issue_carat{get;set;}
public int issue_pieces{get;set;}
public int receive_pieces{get;set;}
public double receive_carat{get;set;}
public int kp_pieces{get;set;}
public decimal kp_carat{get;set;}
public double loss{get;set;}
public string issue_manager{get;set;}
public string issue_by{get;set;}
public string receive_by{get;set;}
public int status{get;set;}
public DateTime receive_date{get;set;}
public string receive_time{get;set;}
public int factory_id{get;set;}
}
List<issuereceive_manageroffice_bal> issue_receive_list = new List<issuereceive_manageroffice_bal>();
issue_receive_list =
(from DataRow dr in DataTable.Rows
select new issuereceive_manageroffice_bal()
{
issue_id = 0,
issue_time = TimeSpan.Parse("0"),
receive_time = null,
shape = null,
process = dr["process"].ToString(),
issue_date = Convert.ToDateTime(dr["issue_date"]),
eg_no = dr["eg_no"].ToString(),
lotno = dr["lotno"].ToString(),
clarity = dr["clarity"].ToString(),
sieves = dr["sieves"].ToString(),
issue_carat = dr["issue_carat"].ToString() != "" ? double.Parse(dr["issue_carat"].ToString()) : 0,
issue_pieces = dr["issue_pieces"].ToString() != "" ? int.Parse(dr["issue_pieces"].ToString()) : 0,
receive_carat = dr["receive_carat"].ToString() != "" ? double.Parse(dr["receive_carat"].ToString()) : 0,
kp_pieces = dr["kp_pieces"].ToString() != "" ? int.Parse(dr["kp_pieces"].ToString()) : 0,
kp_carat = dr["kp_carat"].ToString() != "" ? decimal.Parse(dr["kp_carat"].ToString()) : 0,
loss = dr["loss"].ToString() != "" ? double.Parse(dr["loss"].ToString()) : 0,
issue_manager = dr["lotno"].ToString(),
issue_by = dr["issue_by"].ToString(),
receive_by = dr["receive_by"].ToString(),
status = dr["status"].ToString() != "" ? int.Parse(dr["status"].ToString()) : 0,
receive_date = Convert.ToDateTime(dr["receive_date"]),
factory_id = dr["factory_id"].ToString() != "" ? int.Parse(dr["factory_id"].ToString()) : 0,
}).ToList();