terça-feira, 8 de dezembro de 2009

ADO.NET Entity Framework - Como gerar um DataTable a partir de uma query LINQ

Olá Pessoal

Neste Post, vou demonstrar como podemos gerar um DataTable a partir de uma query realizada com o LINQ utilizando ADO Data Entity.

Em determinadas situações como uma migração de sistema, por exemplo, necessitamos adaptar nosso código para trabalhar da maneira mais transparente possível e causando um menor impacto na aplicação. Vou dar um exemplo. Imagine uma aplicação onde a camada de dados faz um acesso a base e monta um DataTable devolvendo para a aplicação consumir. Nesta aplicação temos a necessidade de fazer uma migração para ADO.NET Entity Framework. Seria um impacto muito grande se, a princípio, tentássemos migrar a camada de dados e a aplicação junto para que, ao invés de DataTable, a aplicação passe a receber objetos do Entity Framework. Para isso, desenvolví uma pequena rotina para gerar DataTable a partir de uma query LINQ no Entity Framework.


A primeira parte é a query com o LINQ:

AdventureWorksEntities db = new AdventureWorksEntities();
IQueryable _query = (from c in db.Address
                    where c.City.Equals("Bothell", StringComparison.CurrentCultureIgnoreCase)
                    select new
                    {
                        ID = c.AddressID,
                        Endereco = c.AddressLine1,
                        Cidade = c.City,
                        CaixaPostal = c.PostalCode
                    }
                    ).OrderBy(o=>o.Endereco);

Reparem que no Retorno eu recebo um objeto do tipo IQueryable. Ele é uma interface que guarda os dados da query como: provider, expressão e os tipos de elemento que são retornados quando a expressão é executada. Como o retorno da query por definição implementa IQueryable, esta atividade fica transparente. O IQueryable também implementa IEnumerable, o que possibilita também percorrer a lista retornada pela query.



Eis o Método que transforma o objeto IQueriable em DataTable:

public static DataTable RetornarDataTable(IQueryable pObjQuery)
{
    DataTable _dt = new DataTable();

    //Cria o DataTable
    foreach (var item in pObjQuery)
    {
        Type t = item.GetType();
        PropertyInfo[] properties = t.GetProperties();
        for (int i = 0; i < properties.Length; i++)
        {
            _dt.Columns.Add(properties[i].Name);
        }
        break;
    }

    IEnumerator en = (IEnumerator)pObjQuery.GetEnumerator();

    //Popula o DataTable
    while (en.MoveNext())
    {
        Type ten = en.Current.GetType();
        PropertyInfo[] propertiesen = ten.GetProperties();
        DataRow _dtrow = _dt.NewRow();
        for (int i = 0; i < propertiesen.Length; i++)
        {
            object obj = propertiesen[i].GetValue(en.Current, new object[] { });
            _dtrow[i] = obj;
        }
        _dt.Rows.Add(_dtrow);
    }

    return _dt;
}


Reparem que neste código, procurei separar a montagem do DataTable, inclusive com os nomes das colunas fiéis a query, e logo após percorro a lista carregando os registros.


Abaixo o Código Completo para Testes:

static void Main(string[] args)
{
    DataTable _dtEnderecos = RetornarEnderecos();

    foreach (DataColumn col in _dtEnderecos.Columns)
    {
        Console.WriteLine(col.ColumnName);
    }
    Console.WriteLine("");
    foreach (DataRow dados in _dtEnderecos.Rows)
    {
        Console.WriteLine(dados["ID"] + " " + dados["Endereco"] + " " + dados["Cidade"] + " " + dados["CaixaPostal"]);
    }
    Console.Read();
}

public static DataTable RetornarEnderecos()
{
    AdventureWorksEntities db = new AdventureWorksEntities();
    IQueryable _query = (from c in db.Address
                        where c.City.Equals("Bothell", StringComparison.CurrentCultureIgnoreCase)
                        select new
                        {
                            ID = c.AddressID,
                            Endereco = c.AddressLine1,
                            Cidade = c.City,
                            CaixaPostal = c.PostalCode
                        }
                        ).OrderBy(o=>o.Endereco);

    return RetornarDataTable(_query);
}
public static DataTable RetornarDataTable(IQueryable pObjQuery)
{
    DataTable _dt = new DataTable();

    //Cria o DataTable
    foreach (var item in pObjQuery)
    {
        Type t = item.GetType();
        PropertyInfo[] properties = t.GetProperties();
        for (int i = 0; i < properties.Length; i++)
        {
            _dt.Columns.Add(properties[i].Name);
        }
        break;
    }

    IEnumerator en = (IEnumerator)pObjQuery.GetEnumerator();

    //Popula o DataTable
    while (en.MoveNext())
    {
        Type ten = en.Current.GetType();
        PropertyInfo[] propertiesen = ten.GetProperties();
        DataRow _dtrow = _dt.NewRow();
        for (int i = 0; i < propertiesen.Length; i++)
        {
            object obj = propertiesen[i].GetValue(en.Current, new object[] { });
            _dtrow[i] = obj;
        }
        _dt.Rows.Add(_dtrow);
    }

    return _dt;
}

A codificação é relativamente simples e de fácil implementação.

Até a Próxima...