微软的Reporting Services逐渐被大众熟悉并接受。以前做过Reporting Services的项目,但是总结的不多。最近看到越多越多的报表项目是用Reporting Services来做,走出去看看才知道别人在做什么。
今天要涉及到的主题是Reporting Services Extension,报表扩展。其实我是要搭建一个报表设计,开发的工具箱。在以前的一篇文章中,提到过用Reporting Services+Remoting做的报表设计平台。Remoting客户端向服务器发送SQL语句,其实不是SQL语句,准确说是类似如下的语句:程序集;类;方法 把这个查询语句发送到Remoting服务器,服务器在GAC中去找对应的程序集和方法,反射调用之,返回数据集给报表设计器。这种结构的报表设计灵活性很强。特别是一些计算,字符串方面的处理,用SQL语句可能不方便,要写很多的T_SQL代码。但是如果用这种方法,充分发挥程序语言(C#)和数据库语言(T_SQL)的长处,把集合运算部分用T_SQL来处理,计算和字符文本的处理则留在C#代码中。
万丈高楼平地起,要做这样一个扩展,需要先熟悉Reporting Services提供了哪些可以扩展的方面。
如果您看过微软的例子,它提供了下面几个主要的例子(Extension Samples)
FormsAuthentication Sample 扩展报表服务器的验证方式
FsiDataExtension Sample 数据处理扩展
PolygonsCustomReportItem 自定义报表项
PrinterDelivery Sample 打印传送扩展
我主要关注的是FsiDataExtension ,它的位置在
C:\Program Files\Microsoft SQL Server\90\Samples\Reporting Services\Extension Samples
代码结构如下图
这里面有几个主要的类:Connection,Command,DataParameter,DataReader,Transaction。
看到这几个类,您可能会心的一笑,这不就是ADO.NET的结构吗?
编译这个例子,生成程序集Microsoft.Samples.ReportingServices.FsiDataExtension.dll
部署这个例子
1)拷贝文件到报表服务器和设计器中,把Microsoft.Samples.ReportingServices.FsiDataExtension.dll拷贝到C:\Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer\bin.
和C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies
2)修改配置文件RSReportServer.config and RSReportDesigner.config的Data元素
RSReportDesigner.config 文件的位置是 C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies。RSReportServer.config 位置在C:\Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer
添加如下元素
<Extension Name="FSI" Type="Microsoft.Samples.ReportingServices.FsiDataExtension.FsiConnection,
Microsoft.Samples.ReportingServices.FsiDataExtension"/>
为了支持报表设计器,修改RSReportDesigner.config 的Designer元素,添加
<Extension Name="FSI" Type="Microsoft.ReportingServices.QueryDesigners.VDTQueryDesigner,
Microsoft.ReportingServices.QueryDesigners"/>
3)配置代码访问安全性
修改rssrvpolicy.config和rspreviewpolicy.config文件,它们的位置分别在在
C:\Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer
和C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies
添加一个CodeGroup 元素,内容如下
<CodeGroup class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="FSICodeGroup"
Description="Code group for my FSI data processing extension">
<IMembershipCondition class="UrlMembershipCondition"
version="1"
Url="C:\Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer\bin\Microsoft.Samples.ReportingServices.FsiDataExtension.dll"
/>
</CodeGroup>
不需要重启动电脑,打开Visual Studio 2005 BIDS,新建一个Reporting Services项目
从图中看到,数据源中有File Share Information,选择它作为报表的数据源
新建数据集,输入查询字符串
\\XX-PCName\\2010
,XX-PCName是计算机的名字
这个扩展的目的是读取某台计算机的共享文件夹中的信息,作为数据集的内容。
执行这个数据集,得到的结果如下
设计报表,使用Table控件
预览报表,结果如预期所示
虽然这个扩展没有多大的意义,但是它展示了如何去扩展Reporting Services的Data Process。
下面再来看看,FsiDataExtension项目的代码结构
再看看类图,大概了解有多少个类相互协作
再到代码里面看看,各自的关系
public sealed class FsiConnection : IDbConnectionExtension
public sealed class FsiCommand : IDbCommand
public sealed class FsiDataParameter : IDataParameter
public sealed class FsiDataParameterCollection : ArrayList, IDataParameterCollection
public sealed class FsiDataReader : IDataReader
public sealed class FsiTransaction : IDbTransaction
要正确编译这些类,需要引用Microsoft.ReportingServices.Interfaces.dll,实现它的接口
运用熟悉的ADO.NET的知识,打开IDbConnection接口
IDbCommand的结构如下
通过代码中的注释,一个查询语句传进来,它的流程应该是这样的
1)连接类型是FsiConnection,它是个空连接,不需要传入任何连接字符串。
因为权限的原因,设置当前连接主体WindowsIdentity
2)引擎根据查询代码和连接类型,调用如下构造方法
public FsiCommand(string cmdText, FsiConnection connection) {
m_cmdText = cmdText; m_connection = connection;
}
最主要的还是IDbCommand.ExecuteReader(CommandBehavior behavior),它是执行查询命令的主要地方,它调用FsiDataReader,读取数据记录
reader = new FsiDataReader(m_cmdText);
reader.GetDirectory(m_cmdText);
进入数据读取类,它的读取代码如下
bool IDataReader.Read()
{
if (m_ie != null)
{
bool notEOF = m_ie.MoveNext();
if (notEOF == true)
{
m_currentRow++;
if (m_fsi[m_currentRow] is FileInfo)
{
FileInfo f = (FileInfo)m_fsi[m_currentRow];
m_cols[0] = f.Name;
m_cols[1] = f.Length.ToString(
System.Globalization.CultureInfo.InvariantCulture);
m_cols[2] = "File";
m_cols[3] = f.CreationTime.ToString();
}
else
{
DirectoryInfo d = (DirectoryInfo)m_fsi[m_currentRow];
m_cols[0] = d.Name;
m_cols[1] = "0";
m_cols[2] = "Directory";
m_cols[3] = d.CreationTime.ToString();
}
}
return notEOF;
}
return false;
}
FsiDataReader定义了如下成员
internal String[] m_names = { "Name", "Size", "Type", "CreationDate" };
internal Type[] m_types = { typeof(String), typeof(long), typeof(String), typeof(DateTime) };
internal object[] m_cols = new object[4];
它有一个方法,根据传入的查询字符串,读取目录信息
internal void GetDirectory(string cmdText)
{
ValidateCommandText(cmdText);
m_dir = new DirectoryInfo(cmdText);
m_fsi = m_dir.GetFileSystemInfos();
m_currentRow = -1;
m_ie = m_fsi.GetEnumerator();
}
这样,借助于这些方法和接口,Fsi DataExtension Sample就可以部署到Reporting Services中,供报表设计和运行时使用。更多的细节请查看具体的代码。
我的主要目的是开头说到的,做开篇所说的那样的一个报表设计扩展。
如果有不熟悉的接口,请查阅MSDN。我根据自己的理解来论述,不恰当或错误之处欢迎批评指正。
Reporting Services Extension:File Share Data Processing Extension全程指南