目录

介绍

先决条件

战略

使用代码

以终为始

故事的其余部分

从RDLC文件中获取查询

设置任何参数

重构

报表参数/查询参数

网址参数

运行查询并填写数据表

跟进(重构)

更新:导致错误的其他事情

结论


  • 下载 VStudio 2010 的旧源 - 21.2 KB  或
  • 下载 VStudio 2013 的源代码 - 179.6 KB

这是一个常见的场景:您有一个用ASP.NET(或MVC或SharePoint)编写的网站,并且您想显示一些报告。您可能正计划编写一些新报告,并且您正在尝试决定使用哪种技术,或者您可能有几个之前已经制作的SSRS报告,并且您希望从您的ASP.NET站点运行它们。

CodeProject上有很多很好的文章,展示了如何使用RDLC文件运行报表,无论是来自ASP.NET还是WinForms等。阅读它们之后,您的选择似乎是:

  1. 将报表放入SSRS并使用ReportViewer控件调用SSRS运行您的报表
  2. .RDL.RDLC文件添加到您的项目中,并创建一些对象来存放数据,以便报表具有与数据库的接口,我想要第三种选择:
  3. 只需像SSRS一样运行它,但无需安装SSRS服务器。我说的是最低限度的足迹。我只想将.RDL.RDLC文件的名称传递给ASPX页面并让它运行。这就是SSRS的做法。我应该也能做到。

这是第三种选择的实现。

先决条件

  1. 要运行它,您仍然需要安装报告运行时。您可以将其添加为Nuget包“Microsoft.ReportViewer.Runtime.Common”和“Microsoft.ReportViewer.WebForms”(或WinForms)。Microsoft 下载站点上提供了可再发行组件。
  2. 我有一些旧报告,它们是用旧的商业智能开发工作室(2008)制作的。我还有一些使用ReportBuilder(内置SSRS网络)制作的新报告。此解决方案适用于任何一个。

战略

.RDLC文件都是XML 如果您使用记事本打开它并检查内容,您会看到XML描述了显示/设计,但它还包含其他一些有用的特征。其中之一是报表的查询(获取数据)。

我的策略是提取数据库查询,设置任何参数,运行查询,将结果存储在DataTable(s)中并将其提供给报告。

我的目标是从通用页面运行我的报告,并通过URL QueryString传递报告名称和任何查询参数,如下所示:

.../View.aspx?Report=Example.rdlc&StartDate=1/1/2012&EndDate=12/31/2012

为简单起见,我将只使用应用程序其余部分使用的相同DB连接字符串,但我会将其包装在本地工厂方法中,以实现可维护性。

使用代码

以终为始

这就是我开始的地方。这些示例效果很好,但无法使用嵌入在报告中的查询。在代码块(如下)中,您可以看到我创建了一个名为Report (命名空间是RDL)的类来封装RDLC的内容/结构。我的RDL.Report类还包含一个工厂方法来帮助将XML转换为对象。

//View.aspx.cs
protected void ShowReport()
{System.IO.FileInfo reportFullPath = this.ReportFile;//check to make sure the file ACTUALLY exists, before we start working on itif (reportFullPath != null){//map the reporting engine to the .rdl/.rdlc filervReportViewer.LocalReport.ReportPath = reportFullPath.FullName;  // 1. Clear Report DatarvReportViewer.LocalReport.DataSources.Clear();// 2. Get the data for the report// Look-up the DB query in the "DataSets" // element of the report file (.rdl/.rdlc which contains XML)RDL.ReportreportDef = RDL.Report.GetReportFromFile(reportFullPath.FullName);// Run each query (usually, there is only one) and attach it to the reportforeach (RDL.DataSet ds in reportDef.DataSets){//copy the parameters from the QueryString into the ReportParameters definitions (objects)ds.AssignParameters(this.ReportParameters);//run the query to get real data for the reportSystem.Data.DataTable tbl = ds.GetDataTable(this.DBConnectionString);//attach the data/table to the Report's dataset(s), by nameReportDataSource rds = new ReportDataSource();rds.Name = ds.Name; //This refers to the dataset name in the RDLC filerds.Value = tbl;rvReportViewer.LocalReport.DataSources.Add(rds);}rvReportViewer.LocalReport.Refresh();}
}  

故事的其余部分

(上面的)代码块显示了应用程序的核心;运行查询并将数据附加到报表,然后运行报表。现在,让我们看看获取数据的部分。

RDLC文件中获取查询

.RDLC文件中,查询的XML如下所示(删除其他所有内容后):

<Report><DataSets><DataSet Name="IrrelevantToThisExample"><Query><DataSourceName>DataTableName</DataSourceName><CommandText>SELECT * FROM sys.Tables</CommandText></Query></DataSet></DataSets>
</Report>

在我的第一次尝试中,我使用XPath从XML(在RDLC文件内部)中提取查询。它适用于简单的查询。但是,我意识到如果查询有任何参数(或存储过程等),事情就会变得一团糟。

在我的第二次尝试中,我采取了不同的方法。我意识到如果我将XML反序列化为一堆对象,代码会更容易。这听起来既复杂又可怕,但是一旦你看到它,你就会意识到XML序列化/反序列化是多么简单。

与此XML匹配的(简化的)类如下所示:

[Serializable(), System.Xml.Serialization.XmlRoot("Report")]
public class Report : SerializableBase
{public List<DataSet> DataSets = new List<DataSet>();
}public class DataSet
{[System.Xml.Serialization.XmlAttribute]public string Name;public Query Query = new Query();
}public class Query
{public string DataSourceName;public string CommandText;
}

反序列化XML后,您可以轻松提取查询,如下所示:

Report report =Report.Deserialize(xml, typeof(RDL.Report));
String commandText = report.DataSets[0].Query.CommandText;

该SerializableBase对象是我从几个项目中重复使用的东西。它使将任何对象序列化或反序列化为XML变得简单,反之亦然。这是代码:

[Serializable]
public class SerializableBase
{public static SerializableBase Deserialize(String xml, Type type){//… some code omitted for brevity. See downloads.System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(type);using (System.IO.StringReadersr = new System.IO.StringReader(xml)){return (SerializableBase)ser.Deserialize(sr);}}
}  

设置任何参数

正如我前面提到的,在我处理参数化查询和存储过程之前,代码非常简单。我不得不添加更多的反序列化类。为简洁起见,我会将它们包含在下载的代码中,但可以免去您在此处阅读代码的麻烦。别担心。它们是非常简单(无聊)的类,与XML的结构相匹配,就像上面的序列化类一样。

重构

此代码的其余部分从实用程序类开始。看完之后,我意识到如果我将实用程序代码封装在序列化类中作为方法而不是作为外部辅助实用程序函数,那将更加纯粹地面向对象。它使序列化类看起来更复杂。这就是为什么在本文中,我首先以最简单的形式描述原始类(如上)。

报表参数/查询参数

不幸的是,在RDLC文件中,查询块定义了它的参数,但没有为它们定义类型。DB会阻塞不容易转换的类型,例如:DateTime、Numeric和Integer。幸运的是,参数类型在RDLC的XML的单独部分中定义。我只需要将它们复制到查询参数定义中。不幸的是,它使代码看起来有点老套,但它确实可靠地完成了工作。

//Report.cs
private void ResolveParameterTypes()
{//for each report parameter, find the matching query parameter and copy-in the data typeforeach (ReportParameter rParam in this.ReportParameters){foreach (DataSet ds in this.DataSets)foreach (QueryParameter qParam in ds.Query.QueryParameters){if (qParam.Value == "=Parameters!" + rParam.Name + ".Value"){qParam.DataType = rParam.DataType;}}}
}
// override the constructor so the report param types are always resolved to the query params
//as a bonus, now you don't have to cast it after deserializing it
public static Report Deserialize(string xml, Type type)
{Report re;re = (Report)SerializableBase.Deserialize(xml, type);//copy the type-names from the ReportParameters to the QueryParametersre.ResolveParameterTypes();return re;
}

网址参数

现在,我将(URL)QueryString中的参数复制到报告的param中。自然,我对与报告中的QueryString参数名称匹配的参数名称做出了一些重大假设。如果它们不匹配,则会出现错误,但应该很容易找出问题所在。我还可以添加一些诊断程序来检测哪些参数没有获得分配给它们的值(可能稍后)。

//View.aspx.cs
private System.Collections.Hashtable ReportParameters
{get{System.Collections.Hashtable re = new System.Collections.Hashtable();//gather any params so they can be passed to the reportforeach (string key in Request.QueryString.AllKeys){if (key.ToLower() != "path")//ignore the "path" param. It describes the report’s file path{re.Add(key, Request.QueryString[key]);}}return re;}
}//DataSet.cs
public void AssignParameters(System.Collections.HashtablewebParameters)
{foreach (RDL.QueryParameter param in this.Query.QueryParameters){string paramName = param.Name.Replace("@", "");//if this report param was passed as an arg to the report, then populate itif (webParameters[paramName] != null)param.Value = webParameters[paramName].ToString();}
}

运行查询并填写数据表

这是很基本的。设置命令对象,添加参数,然后只需使用DataAdapter来填充表格。

//DataSet.cs
public System.Data.DataTable GetDataTable(string DBConnectionString)
{System.Data.DataTable re = new System.Data.DataTable();using (System.Data.OleDb.OleDbDataAdapter da = new System.Data.OleDb.OleDbDataAdapter(this.Query.CommandText, DBConnectionString)){if (this.Query.QueryParameters.Count > 0){foreach (RDL.QueryParameter param in this.Query.QueryParameters){string paramName = param.Name.Replace("@", "");//OLEDB chokes on the @symbol, it prefers ? marksusing (System.Data.OleDb.OleDbCommand cmd = da.SelectCommand)cmd.CommandText = cmd.CommandText.Replace(param.Name, "?");using (System.Data.OleDb.OleDbParameterCollection params = da.SelectCommand.Parameters)switch (param.DataType){case "Text":params.Add(new OleDbParameter(paramName, OleDbType.VarWChar) { Value = param.Value });break;case "Boolean":params.Add(new OleDbParameter(paramName, OleDbType.Boolean) { Value = param.Value });break;case "DateTime":params.Add(new OleDbParameter(paramName, OleDbType.Date) { Value = param.Value });break;case "Integer":params.Add(new OleDbParameter(paramName, OleDbType.Integer) { Value = param.Value });break;case "Float":params.Add(new OleDbParameter(paramName, OleDbType.Decimal) { Value = param.Value });break;default:params.Add(new OleDbParameter(paramName, param.Value));break;}}}da.fill(re);re.TableName = this.Name;return re;
}

跟进(重构)

我确实重构了这段代码(在下载中),这使它有点混乱。我想让它变得灵活,这样我就可以在多个项目中使用它。由于我无法确定db连接字符串将始终是OLEDB或SqlClient连接,因此我检查了连接字符串并为其中任何一个使用了适当的库集(OLEDB/SQLClient)。代码长度增加了一倍,但更便携。

更新:导致错误的其他事情

一位朋友帮我运行了一些测试报告,结果表明如果出现问题,该ReportViewer控件不会生成任何好的/有用的错误消息。相比之下,我放在View页面的“Download”按钮,在处理过程中很容易报错。从中,我学到了一些东西:

  1. 如果报表具有外部图形或报表部件,则这些文件需要可用(也就是说,您也需要/reports文件夹中的这些文件)并且在正确的路径中。对于我的测试示例,报告使用文件夹/Reports/Web Parts/中的图形。
  2. 如果报告具有必需的参数(或不是“可选的”),则必须提供这些参数,否则报告将不会运行。
  3. 参数可以区分大小写。也许它只是.NET。无论哪种方式,如果您的报告无法运行,请检查以确保您提供的是“PrintID”或“PrintId”,而不仅仅是“printid”。

最后,我最近更新了这个示例以使用Visual Studio 2013运行。我添加了一个“诊断”页面,以检查一些设置。我改进了“下载”选项。我改进了这个例子来处理外部图像,并检测所需的报告参数。

我希望你发现它对你更有效。如果没有,我会很感激反馈,甚至可能是一个示例文件(.rdl),这样我就可以解决任何错误。

结论

这就是从RDLC文件中提取查询并在ASP.NET中运行它所需的全部内容。

SSRS最初是由Microsoft编写的,作为如何使用这些技术来完成我在此处展示的内容的示例。当然,SSRS的许多功能远远超出了我所展示的范围,但如果您不需要所有这些丰富的功能,那么此代码对于您和您的.NET项目来说应该是快速且可移植的。

https://www.codeproject.com/Articles/607382/Running-a-RDL-RDLC-SQL-Report-in-ASP-NET-without-S

在没有SSRS的ASP.NET中运行RDL/RDLC(SQL报告)相关推荐

  1. Asp.Net 中Report Service (RDLC)动态绑定数据-学习笔记

    Asp.Net 中Report Service (RDLC)动态绑定数据-学习笔记 1)托拽ReportViewer控件到aspx页面,此时,系统会自动添加相关引用,修改Web.config设置: 2 ...

  2. asp.net中ADO.NET连接SQL数据库代码和连接Access数据库代码

    连接SQL数据库方法: 一.建立连接          1.(使用System.Data.SqlClient) <1>  string strcon;         //声明连接字串   ...

  3. SQL Server LocalDB 在 ASP.NET中的应用

    SQL Server LocalDB 在 ASP.NET中的应用. 使用SQL Server LocalDB的优势: 快速部署完整的SQL Server.以后项目可以无缝升级到高级版本. 它是真正的S ...

  4. 在 Linux“.NET研究” 操作系统中运行 ASP.NET 4 (下)

    "在 Linux 操作系统中运行 ASP.NET 4 (中)"中已经配置好了 openSUSE 11.3 操作系统. 现在,我们进入"GNOME 终端",使用 ...

  5. ASP.NET Core 网站在Docker中运行

    Docker作为新一代的虚拟化方式,未来肯定会得到广泛的应用,传统虚拟机的部署方式要保证开发环境.测试环境.UAT环境.生产环境的依赖一致性,需要大量的运维人力,使用Docker我们可以实现一次部署, ...

  6. 在Linux和Windows的Docker容器中运行ASP.NET Core

    译者序:其实过去这周我都在研究这方面的内容,结果周末有事没有来得及总结为文章,Scott Hanselman就捷足先登了.那么我就来翻译一下这篇文章,让更多的中文读者看到.当然Scott遇到的坑我也遇 ...

  7. 在docker中运行ASP.NET Core Web API应用程序

    本文是一篇指导快速演练的文章,将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤,在介绍的过程中,也会对docker的使用进行一些简单的描述.对于.NET Cor ...

  8. 在Docker中运行ASP.NET Web API解决方案

    目录 介绍 先决条件 如何容器化现有项目 添加docker-compose项目 带有docker-compose的容器化解决方案 添加环境变量 后端 前端 不使用Visual Studio运行您的应用 ...

  9. 在ASP.NET中如何运行后台任务

    from:https://blogs.msdn.microsoft.com/scott_hanselman/2014/12/21/asp-net/ [原文发表地址] How to run Backgr ...

  10. ASP.NET中 RequiredFieldValidator(非空验证)的使用

    ylbtech-ASP.NET-Control-Validator: RequiredFieldValidator(非空验证)的使用 ASP.NET中 RequiredFieldValidator(非 ...

最新文章

  1. 多线程实现生产者消费者模型
  2. 迁移Win 2003 DHCP服务到2008R2
  3. python if语句多个条件-Python中if有多个条件处理方法
  4. 2.18 Logistic 损失函数的解释-深度学习-Stanford吴恩达教授
  5. Fifth Week:Node.js学习
  6. “面试不败计划”:垃圾垃圾回收
  7. android广告平台的介绍
  8. CCPlace,CCFlip*,CCToggleVisibility,CCMoveTo*,CCJumpTo*,CCScale*,CCRotate*,CCSkew*,fade,CCCardinalSp*
  9. 谈谈Spring中都用到了那些设计模式
  10. docker for ubuntu安装
  11. bootstrap 模态框满屏_解决Ueditor在bootstarp 模态框中全屏问题
  12. Spring Cloud微服务之Gateway网关(十三)
  13. 姿态估计:人体骨骼关键点检测综述(2016-2020)
  14. UML教程8:构件图 部署图 附录
  15. 高等数学复盘 | 第七册下册第八章——向量代数与空间解析几何思维导图梳理(复习专用)
  16. 那些年曹大写过的博客
  17. 移动安全工具-apktool
  18. 用Java写小学生算术题
  19. Matlab中inv函数的使用
  20. 人脸识别-批量裁剪图像

热门文章

  1. Ubuntu 串口调试
  2. (3).Mybatis动态sql的使用
  3. ABBYY FineReader 12使用教程
  4. python画一个正方形和圆_python用正方形画圆|怎么用matlab画出一个正方形?
  5. 华硕支持2003服务器主板,驱动天空 - 品牌主板 - 服务器主板 SERVER - 华硕服务器主板...
  6. linux中apache无法启动,Apache无法启动
  7. C++实现通讯录管理系统
  8. mybatis 批量新增 批量修改
  9. 平面设计完全手册_什么是平面设计,做平面设计都要了解哪些基础知识点?
  10. 回顾15个月的工作经历