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 html : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
 
<%@ Page Language="C#" AutoEventWireup="true" Inherits="CustomPages.PageRDLtoPDF" %>

PageRDLtoPDF.aspx.cs
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
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 csharp : Sélectionner tout - Visualiser dans une fenêtre à part
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).