Récupérer la requête d'un DataSource depuis un RLDC, lui passer des paramètres, et l'exécuter
Bonjour,
Je travaille sur une librairie qui puisse permettre, depuis une page ASPX, de générer à la volée un document PDF en lui passant en paramètre :
- Une série de paramètres
- Le nom d'un rapport RDLC ou RDL présent sur le serveur
Pour le moment, j'ai fait un truc qui marche plutôt pas mal, mais il est à mon avis largement perfectible.
Le code en l'état :
PageRDLtoPDF.aspx
Code:
1 2
|
<%@ Page Language="C#" AutoEventWireup="true" Inherits="CustomPages.PageRDLtoPDF" %> |
PageRDLtoPDF.aspx.cs
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
using My.Editions;
using System;
using System.IO;
using update.Web.Pages;
namespace CustomPages
{
public class PageRDLtoPDF : BasePage
{
public PageRDLtoPDF()
{
}
protected void Page_Load(object sender, EventArgs e)
{
Response.Clear();
Response.ContentType = "application/pdf";
IniFile iniFile = new IniFile(Server.MapPath("~/system/sys/mmdb.ini"));
MemoryStream memoryStream = new MemoryStream();
RDLtoPDF edition = new RDLtoPDF(iniFile.keys["CnxString"], iniFile.keys["TblPrefix"], Server.MapPath("~/"), Request.Form["Records"], Request.Form["ReportName"]);
edition.Edition(memoryStream);
memoryStream.WriteTo(Response.OutputStream);
Response.Flush();
Response.Close();
Response.End();
}
}
} |
RDLtoPDF.cs
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
using System.IO;
using Microsoft.Reporting.WinForms;
namespace My.Editions
{
public class RDLtoPDF : IEdition
{
private readonly string CnxString;
private readonly string TblPrefix;
private readonly string Path;
private readonly string ReportName;
private readonly string RecordsId;
public RDLtoPDF(string cnxString, string tblPrefix, string path, string recordsId, string reportName)
{
CnxString = cnxString;
TblPrefix = tblPrefix;
Path = path;
ReportName = $"{Path}\\my_ssrs\\{reportName}";
RecordsId = recordsId;
}
public void Edition(Stream output)
{
if (RecordsId.Length > 0)
{
using (OdbcConnection cnx = new OdbcConnection(CnxString))
{
cnx.Open();
using (OdbcCommand cmd = cnx.CreateCommand())
{
ReportViewer viewer = new ReportViewer();
viewer.LocalReport.Refresh();
viewer.LocalReport.ReportPath = ReportName;
IList<string> Queries = viewer.LocalReport.GetDataSourceNames();
cmd.CommandTimeout = 120;
foreach (string query in Queries)
{
cmd.CommandText = string.Format(File.ReadAllText(string.Concat($"{Path}\\my_ssrs\\{query}.sql")), TblPrefix, RecordsId);
cmd.Prepare();
DataSet ds = new DataSet();
OdbcDataAdapter da = new OdbcDataAdapter(cmd);
da.Fill(ds);
ReportDataSource rds = new ReportDataSource(query, ds.Tables[0]);
viewer.LocalReport.DataSources.Add(rds);
}
viewer.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;
byte[] bytes = viewer.LocalReport.Render("PDF", null, out string mimeType, out string encoding, out string extension, out string[] streamIds, out Warning[] warnings);
output.Write(bytes, 0, bytes.Length);
}
cnx.Close();
}
}
}
private void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
using (OdbcConnection cnx = new OdbcConnection(CnxString))
{
cnx.Open();
using (OdbcCommand cmd = cnx.CreateCommand())
{
foreach (ReportParameterInfo p in e.Parameters)
{
cmd.Parameters.AddWithValue(p.Name, p.Values[0]);
}
foreach (string query in e.DataSourceNames)
{
cmd.CommandText = string.Format(File.ReadAllText(string.Concat($"{Path}\\my_ssrs\\{query}.sql")), TblPrefix);
cmd.Prepare();
DataSet ds = new DataSet();
OdbcDataAdapter da = new OdbcDataAdapter(cmd);
da.Fill(ds);
ReportDataSource rds = new ReportDataSource(query, ds.Tables[0]);
e.DataSources.Add(rds);
}
}
cnx.Close();
}
}
}
} |
La partie sur les sous-rapport, pour le moment je ne l'ai pas testée.
En revanche, le reste fonctionne.
Si j'ai un rapport avec 3 datasource, ma page arrive à retrouver le nom de ces trois datasource, ainsi que les paramètres.
En revanche, je n'arrive pas à récupérer le SQL des datasources présents dans le rapport.
Du coup je suis obligé de passer par des fichiers SQL stockés à côté, portant le nom des datasets.
C'est un peu crade, et j'aimerais plutôt récupérer les requêtes telles qu'existantes dans le rapport.
Avez-vous une idée de comment faire ?
Autre solution, mais pas forcément meilleure pour moi : exécuter directement le rapport en lui passant les paramètres, sans avoir à me soucier de la liaison aux données.
Seul hic, entre DEV, TEST et PROD ça m'obligerait à maintenir 3 versions différentes des mêmes rapports, ce qui ne me semble pas terrible !
(Là, je retrouve la chaîne de connexion à la base à partir d'un fichier INI trouvé dans un dossier de l'application appelante).