20 maio, 2009

Usando relatórios Embedded Resource com o Reporting Viewer

Primeira vez que eu uso o Reporting Viewer, ou MS Reporting para WinForms, antes eu criava relatórios com o Crystal Reports. 
Só para explicar o por que dessa mudança: não uso DataSet’s (!), trabalho com o objetos POCO, mais explicações siga os posts deste blog sobre a montagem do sistema ABC (que eu promote que vai recomeçar em breve), e o Crystal tem uma limitação para trabalhar para usar uma fonte de dados como a seguinte:

  1. public class CtaPagar {
  2.  
  3.     public virtual int CdCtaPagar { get; set; }
  4.     public virtual DateTime? DtCadastro { get; set; }
  5.     public virtual DateTime DtAlteracao { get; set; }
  6.     public virtual Empresa Credor { get; set; }
  7.     public virtual string Descricao { get; set; }
  8.     public virtual decimal VlTotalPagar { get; set; }
  9.  
  10.     //métodos ...
  11. }

Como Credor é um objeto o Crystal Reports não consegue usar as propriedades do objeto (até a versão que acompanha o VS.Net 2005 Professional), então tive que criar DataSet’s para converter esses meus objetos, isso é ruim, pois é mais um processo dentro do software, mais um código que fica espalhado e eu tenho que manter. Portanto resolvi testar o Reporting Viewer, e ele aceita objetos complexos na minha fonte de dados.

Como estou estudando DDD e já estou aplicando os conceitos me surgiu uma dúvida, onde colocar o relatório? Bom… Não vou discutir esse assunto, o fato é que o relatório ficou próximo a minha camada Model, e daí surgiu o problema de que o Reporting Viewer não funciona como o CR (Crystal Reports), onde o relatório é apenas uma classe e ao instâcia-la eu passo para o visualizador e ele cria o relatório.  O RV (Reporting Viewer) é o componente de visualização do relatório e o relatório é um arquivo RDLC.

Nota: Um arquivo RDLC é básicamente um XML, a MS criou essa linguagem para servir como uma linguagem comum de relatórios, você pode baixar essa especificação aqui.

Agora vamos ao ponto, se eu tenho um arquivo RLDC em um assembly, como Embedded Resource (distribuir em arquivos separados eu particularmente acho ruim pois o arquivo fica sujeito a modificações) como fazer para passar para outra “camada” da aplicação?

Vamos por partes…

Lendo um Embedded Resource

O arquivo esta dentro do assembly, então você não poderá acessá-lo diretamente, para isso você vai ter que ler o assembly, achar o arquivo e pegá-lo. Então será necessário o uso de Reflection, veja o código abaixo:

  1. Assembly a = Assembly.GetExecutingAssembly();
  2.  
  3. Stream rptStream = a.GetManifestResourceStream("Financeiro.Model.Reports.rptCtaPagar.rdlc");
  4.  
  5. return rptStream;

A classe Assembly esta no namespace System.Reflection, na linha 1, o método retorna o assembly que esta rodando o código no momento, o meu arquivo de relatório esta nele.
O componente de visualização RV recebe o relatório como string, mas para ler o arquivo temos que criar um Stream, o método da linha 3, lê um arquivo do tipo Resource no assembly, note que eu passei o nome do relatório com o seu namespace completo, sem isso ele não irá achar o arquivo.

Tendo esse Stream eu posso passar ele para a camada de Visualização do meu software, que é onde o componente RV esta. Veja abaixo:

  1. public frmReportViewer(Stream rptStrem, ReportDataSource[] rptDataSources, ReportParameter[] rptParams)
  2.     : this() {
  3.  
  4. Array.ForEach(rptDataSources, this.reportViewer.LocalReport.DataSources.Add);
  5.  
  6. this.reportViewer.LocalReport.LoadReportDefinition(rptStrem);
  7.  
  8. this.reportViewer.LocalReport.SetParameters(rptParams);
  9.  
  10. }

Este é um construtor de um Form genérico que eu tenho para visualizar relatórios, nele eu recebo um Stream com o relatório, um array de fonte de dados (ou seja posso ter mais de uma fonte de dados) e um array de parâmetros, os dois arrays foram montados antes.
Agora ficou fácil, só preciso dizer para o RV as minhas fontes de dados, linha 4, a definição do relatório, linha 6, e os parâmetros, linha 8, e vóila… ele renderiza o relatório.

Esta é apenas uma sugestão de como fazer uma arquitetura que os relatórios possam ser distribuídos em assemblys, ou seja dentro de dll’s, e o RV não precisa conhecer os relatórios previamente, como sempre é mostrado, através do code snnipt Choose Report do componente RV em modo Design.

Qual a sua maneira?

Nenhum comentário: