Недавно у меня был клиент, у которого было простое, но практичное требование к их отчетам SSRS: они хотели, чтобы все отчеты имели стандартные заголовки и нижние колонтитулы. Однако они хотели использовать один файл шаблона для применения этих заголовков и нижних колонтитулов, и любые обновления шаблона должны отражаться во всех отчетах одновременно. Это представляло интересную задачу, но я смог придумать решение, которое можно применить к полной версии SSRS.
Обзор архитектуры SSRS
Прежде чем перейти к конкретике решения, давайте кратко обсудим архитектуру SSRS. Определения отчетов в SSRS обычно создаются в Visual Studio или Report Builder, и эти файлы на самом деле являются XML-документами, которые ссылается на опубликованную схему, предоставленную Microsoft. SSRS также предлагает мощный API и утилиту командной строки под названием rs Utility, которая может компилировать и запускать файлы кода VB.NET против SOAP API Reporting Services.
Решение
Реализация решения довольно проста. В исходном решении мы обновляли соответствующие XML-элементы в файле .rdlc во время выполнения по определенному шаблону документа. Чтобы применить это к полному серверу отчетов, нам нужно найти шаблонный документ на сервере и применить соответствующие XML-элементы ко всем файлам отчетов в цикле.
Есть несколько вещей, которые следует учитывать при работе с файлами шаблона и отчета .rdl:
- Убедитесь, что все элементы в вашем файле Custom.rdl имеют уникальные имена. SSRS требует, чтобы все элементы в определении отчета имели уникальные имена. Если у вас есть текстовое поле в вашем шаблоне с именем Textbox1, и вы пытаетесь обновить все файлы отчетов, вы можете столкнуться с ошибкой. Рекомендуется называть каждый элемент отчета в заголовке с уникальным идентификатором.
- Избегайте использования встроенных изображений в файле шаблона. Скрипт может работать неправильно, если вы это сделаете. Вместо этого загрузите изображение на сервер отчетов и ссылайтесь на него внешне в определениях отчетов.
Вот код для файла скрипта:
'Скрипт для применения пользовательского заголовка и нижнего колонтитула из файла Custom.rdl, сохраненного на ReportServer, ко всем файлам отчетов на ReportServer
'использует конечную точку Mgmt2010 / исполняемый файл против автономного или интегрированного с SharePoint экземпляра
'Сохраните файл с расширением .rss и запустите его с помощью rs.exe из командной строки.
'Автор Jared Zagelbaum 11/2014 jaredzagelbaum.wordpress.com
Sub Main()
Dim reportDefinition As Byte() = Nothing
Dim doc As New System.Xml.XmlDocument
Dim nsmanager As New XmlNamespaceManager(doc.NameTable)
Dim templateHeader As System.Xml.XmlElement
Dim templateFooter As System.Xml.XmlElement
Dim reportHeader As System.Xml.XmlElement
Dim reportFooter As System.Xml.XmlElement
nsmanager.AddNamespace("rd", "http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition")
Dim items As CatalogItem() = rs.ListChildren("/", True)
'найти файл шаблона
For Each item As CatalogItem In items
If item.TypeName = "Report" And item.Name = "Custom" Then
reportDefinition = rs.GetItemDefinition(item.Path)
Dim stream As New MemoryStream(reportDefinition)
doc.Load(stream)
Exit For
End If
Next
'загрузить шаблонный заголовок / нижний колонтитул в переменную
templateHeader = doc.SelectSingleNode("/rd:Report/rd:ReportSections/rd:ReportSection/rd:Page/rd:PageHeader", nsmanager)
templateFooter = doc.SelectSingleNode("/rd:Report/rd:ReportSections/rd:ReportSection/rd:Page/rd:PageFooter", nsmanager)
'перебор элементов каталога и замена заголовков отчетов шаблоном
For Each item As CatalogItem In items
If item.TypeName = "Report" And item.Name <> "Custom" Then
reportDefinition = rs.GetItemDefinition(item.Path)
Dim stream As New MemoryStream(reportDefinition)
Dim outstream As New MemoryStream()
doc.Load(stream)
reportHeader = doc.SelectSingleNode("/rd:Report/rd:ReportSections/rd:ReportSection/rd:Page/rd:PageHeader", nsmanager)
reportFooter = doc.SelectSingleNode("/rd:Report/rd:ReportSections/rd:ReportSection/rd:Page/rd:PageFooter", nsmanager)
reportHeader.InnerXml = templateHeader.InnerXml
reportFooter.InnerXml = templateFooter.InnerXml
doc.Save(outstream)
reportDefinition = outstream.ToArray()
rs.SetItemDefinition(item.Path, reportDefinition, Nothing)
stream.Dispose()
outstream.Dispose()
End If
Next
End Sub
Сохраните этот скрипт с расширением .rss и выполните его из командной строки на вашем сервере отчетов. Скрипт может работать как с автономными, так и с интегрированными с SharePoint экземплярами SSRS без изменений.
Вывод
API и утилита rs.exe, предоставленные SSRS, предлагают мощный способ автоматизировать задачи и выполнять массовые обновления определений отчетов. Используя эти инструменты, мы смогли научить SSRS новому трюку и применить стандартные заголовки и нижние колонтитулы ко всем отчетам с использованием одного файла шаблона. С некоторыми модификациями этот скрипт можно расширить для добавления общих элементов в отчеты или ссылки на общие наборы данных.
Не забудьте настроить ссылку на пространство имен и XPath, если вы используете более старую версию SSRS. Кроме того, важно отметить, что большая часть работы заключается в понимании того, как эффективно использовать API и утилиту. Надеюсь, это решение будет полезно другим, сталкивающимся с подобными требованиями.