Программное совершенствование отчетов SSRSИсточник: osp Поль Трули
Специализированные сборки для формирования набора данных динамического отчетаПоль Трули Службы SQL Server Reporting Services (SSRS) - платформа для подготовки отчетов корпоративного уровня, размещаемая над широко распространенными службами в серверных технологиях Windows, в том числе SQL Server, ASP.NET и Microsoft. NET Framework. Одно из самых важных качеств SSRS - возможность расширить функции подготовки отчетов с помощью пользовательского кода и программных модулей. В этой статье я поясню, насколько пользовательский код и модули практически применимы при формировании ежедневных отчетов, и расскажу о встроенных модулях SSRS и концепции настраиваемых сборок. Затем будут даны пошаговые инструкции по подготовке специализированной сборки с помощью Visual Studio и ее использования для построения набора данных динамического отчета. Выбор инструментаПрограммисты привыкли использовать инструменты с возможностями объектно-ориентированного проектирования (например, управление экземплярами объектов, интерфейсы, наследование классов) и моделями программирования, управляемыми событиями, для построения пользовательских приложений и компонентов приложений. Важно понимать, что для практического применения SSRS не поддерживает таких программных методов и моделей, и на то есть веские причины. SSRS - платформа подготовки отчетов, а не инструмент программирования. Во время обсуждений методов подготовки отчетов в моем блоге по SQL Server BI и на других форумах мне иногда приходится встречать такие мнения программистов: "Мне не нужен такой инструмент подготовки отчетов, как SSRS. Я могу просто спроектировать специализированное веб-приложение, а затем самостоятельно управлять всем с помощью программных объектов. NET". Я уважаю это мнение и считаю такой подход обоснованным, если требуется управлять каждым элементом пользовательского интерфейса, поведением и взаимодействием. С помощью кода событий и пользовательских элементов управления можно добавить удобные функции, просто недоступные в SSRS. Но при этом необходимо предусмотреть, что произойдет, если объем данных в пользовательском отчете окажется в 100 раз больше ожидаемого или отчет будет запущен несколькими пользователями одновременно. И как удовлетворить потребность пользователя просмотреть отчет в разнообразных форматах? Такие вопросы успешно решаются в SSRS без дополнительных усилий со стороны программиста. Основными компонентами отчета - соединениями данных, выполнением запросов, наборами результатов, кэшированием данных и т.д. - следует управлять с помощью встроенных функций, не вдаваясь в сложное программирование. Важно уметь различать задачи, успешно решенные в SSRS, чтобы не ломать напрасно голову над сложными приемами программирования. МодулиВ основе SSRS лежит расширяемая архитектура. Признаюсь, что начиная работать с SSRS в 2003 году, я был очарован расширяемостью служб и возможностью добавлять программный код в решения для отчетов. Как программист, работающий в. NET, я полагал, что отчеты будут естественным продолжением моих усилий как программиста. Но построив несколько сложных решений для отчетов и работая с другими программистами над архитектурой крупных решений в течение десяти лет, я понял, что огромному большинству решений SSRS не требуется дополнительное программирование. Лучшее применение библиотеки пользовательского кода - расширить встроенные возможности архитектуры SSRS, а не заменить удачные функции. Подготовка пользовательских модулей - непростая задача, и обычно она выходит за рамки практического применения для подготовки корпоративных отчетов. Во внутренних механизмах SSRS используется набор сборок. NET, представляющий собой модули для работы с большинством основных функций. SQL Server версий Standard, Business Intelligence и Enterprise Editions автоматически устанавливает некоторые из этих модулей. Например, при экспорте отчета в Microsoft Excel применяется модуль подготовки отчетов Excel. При планировании подписки модуль доставки электронной почты или файловой системы используется вместо стандартного модуля доставки интерактивных отчетов. В SQL Server версий Standard, Business Intelligence и Enterprise Editions службы SSRS обеспечивают расширение следующими способами.
При необходимости создать пользовательские модули можно учиться на опыте других разработчиков. Я работал с компаниями, в которых SSRS применяется как составная часть набора служб, и некоторые из них создали собственные модули. Одна компания построила специальный модуль безопасности, чтобы пользователи могли выполнять регистрацию и проходить проверку подлинности в ее программе, не прибегая к Windows. Другие компании со сложными уровнями данных и прикладного программирования строят специальные модули обработки данных, которые используются вместо стандартных поставщиков данных в SSRS. Например, одна компания построила модуль для обработки данных, применив его к сложным бизнес-правилам и логике фильтров, специфичным для отрасли. В каждом случае группам программистов потребовались месяцы, чтобы создать продукт, устранить ошибки, протестировать и внедрить специализированные модули в производство. Настраиваемые сборкиСборка - модуль повторно используемого программного кода, обычно подготовленный с помощью языка программирования. NET в среде Visual Studio и часто скомпилированный в DLL-файл, содержащий повторно используемые функции и другую программную логику. Специализированную сборку можно применить, чтобы расширить функциональность отчетов на многие отчеты на одном сервере. Например, если нужно обработать параметрические бизнес-правила, распространяющиеся за рамки возможностей языка запросов, такого как T-SQL или MDX, можно задействовать специализированную сборку для управления запросом к набору данных отчета и связанным поведением отчета. SQL - мощный язык, с помощью которого можно обработать достаточно громоздкую бизнес-логику. При возможности можно задействовать язык запросов - и особенно устойчивые объекты базы данных, такие как хранимые процедуры - для инкапсуляции сложных правил. Лучшие примеры наиболее эффективного применения этого метода гораздо сложнее, чем я привожу в данной статье. Однако показанные приемы представляют интерес и, в некоторых случаях, могут оказаться необходимыми. Создание специализированной сборкиВ следующем примере показано, как построить специализированную сборку, которая собирает инструкцию T-SQL на основе нескольких параметризованных аргументов. Сборка проектируется в среде Visual Studio 2010 в проекте библиотеки классов, который может быть частью того же решения, что и проект отчета. После тестирования сборка развертывается в доверенных папках, где ее можно использовать в SQL Server Data Tools (SSDT) или конструкторе отчетов Business Intelligence Development Studio (BIDS) на сервере отчетов. Проект выполнен в Visual Basic. NET, но с тем же успехом можно использовать C#. Рабочий экземпляр отчета и образцовый программный код проекта можно загрузить, нажав кнопку Download. Создание представления и проекта библиотеки классовПервая задача - создать представление vSalesByTimeAndTerritory в базе данных AdventureWorksDW2012. Убедившись, что вы имеете разрешение на создание объектов в этой базе данных, откройте окно запросов SQL Server Management Studio (SSMS) и выполните сценарий листинга 1, чтобы создать представление. Затем необходимо создать проект библиотеки классов в среде Visual Studio, выполнив следующие действия.
Развертывание библиотеки классовПрежде чем продолжить, отметим два важных обстоятельства. Необходимо найти папки для сборок, которым доверяет Visual Studio и сервер отчетов SSRS. В различных версиях продукта эти пути похожи, но зависят от параметров, выбранных при установке и настройке. После того, как пути найдены, скопируйте их и вставьте в программу Notepad. В этом примере используется путь C:Program Files (x86)Microsoft Visual Studio 10.0Common7IDEPrivateAssemblies для папки сборок Visual Studio 2012. Для сервера отчетов SSRS применяется путь отчетов по умолчанию в SSRS 2012 - C:Program FilesMicrosoft SQL ServerMSRS11.MSSQLSERVERReporting ServicesReportServerbin. Обратите внимание, что если используется SSRS 2012 в SharePoint, то путь по умолчанию - C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions14WebServicesReportingbin. После того, как пути внесены в Notepad, выполните следующие действия. 1. Откройте свойства проекта. В разделе Compile ("Компилировать") выберите Build Events ("События построения"). 2. В текстовом поле Post-build event command line ("Командная строка события после построения") введите следующие команды, указывая пути, скопированные в Notepad: COPY /Y "C:UsersAdministratorDocuments Visual Studio 2010ProjectsReport_Class_LibbinDebug Report_Class_Lib.dll" "C:Program Files (x86) Microsoft Visual Studio 10.0Common7IDEPrivateAssemblies" COPY /Y "C:UsersAdministratorDocuments Visual Studio 2010ProjectsReport_Class_LibbinDebug Report_Class_Lib.dll" "C:Program Files Microsoft SQL ServerMSRS11.MSSQLSERVER Reporting ServicesReportServerbin"Команды показаны разбитыми на несколько строк, но в действительности их нужно вводить в одной строке. В режиме интеграции с SharePoint необходимо также скопировать сборку в общую папку для расширений веб-сервера. Для этого добавьте следующую строку: COPY /Y "C:UsersAdministratorDocuments Visual Studio 2010ProjectsReport_Class_LibbinDebug Report_Class_Lib.dll" "C:Program FilesCommon Files Microsoft SharedWeb Server Extensions14 WebServicesReportingbin"3. Настройте событие после построения для запуска после успешного завершения сборки, как показано на экране 1.
4. Нажмите кнопку OK, чтобы сохранить изменения. 5. Постройте проект заново. С помощью проводника Windows убедитесь, что файл Report_Class_Lib.dll был скопирован куда нужно и имеет недавнюю дату изменения. Создание отчетаТеперь можно создать отчет. К нему предстоит добавить ссылку на сборку (экран 2), параметры и выражение. Выполните следующие действия.
1. Создайте новый отчет с именем Generated Query. 2. Выберите Report Properties ("Свойства отчета") из меню Report ("Отчет"). 3. В диалоговом окне Report Properties выберите страницу References ("Ссылки"). 4. В разделе Add or remove assemblies ("Добавить или удалить сборки") нажмите кнопку Add ("Добавить"). 5. Нажмите кнопку с многоточием (...) справа от новой строки. 6. В диалоговом окне Add Reference ("Добавление ссылки") перейдите на вкладку Browse ("Обзор") и найдите файл Report_ClassLib.dll в папке доверенных сборок. 7. Выделите файл Report_ClassLib.dll и нажмите кнопку OK, чтобы добавить reference. 8. В разделе Add or remove classes ("Добавить или удалить классы") страницы References нажмите кнопку Add. 9. В поле под столбцом Class Name ("Имя класса") введите Report_Class_Lib.ReportLib. 10. В поле под столбцом Instance Name ("Имя экземпляра") введите m_ReportLib. Это псевдоним имени класса, который будет использоваться в отчете. 11. В окне Report Data ("Данные отчета") в левой стороне конструктора отчетов щелкните правой кнопкой мыши узел Parameters, чтобы добавить новый параметр. 13. Воспользуйтесь окном Report Parameters ("Параметры отчета"), чтобы добавить каждый параметр. Используйте информацию из таблицы, чтобы задать имя, приглашение, тип и значение каждого параметра по умолчанию. 14. Для параметра SalesQuery установите видимость Hidden ("Скрытый") на странице General ("Общие") в диалоговом окне Report Parameters ("Параметры отчета"), а затем перейдите на страницу Default Value ("Значение по умолчанию"). 15. Выберите Specific values ("Определенные значения"), чтобы добавить значение по умолчанию. 16. Нажмите кнопку Expression ("Выражение") (fx) справа от раскрывающегося списка. 17. Введите следующий текст, а затем поместите курсор между скобками: =Code.m_ReportLib.SalesQuery( )18. Выберите Parameters ("Параметры") из списка Category ("Категория") и дважды щелкните параметр YearFrom, чтобы добавить reference в выражение. Введите после этого текста запятую с последующим пробелом. 19. Используйте ту же процедуру для ввода дополнительных параметров, необходимых для построения следующего выражения (без возвратов каретки): =Code.m_ReportLib.SalesQuery(Parameters!YearFrom.Value , Parameters!YearTo.Value, Parameters!Country.Value , Parameters!MaxRows.Value, Parameters!IncludeFreight.Value)20. Нажмите кнопку OK, чтобы сохранить выражение. 21. Добавьте текстовое поле к отчету. 22. Щелкните правой кнопкой мыши текстовое поле и выберите пункт Expression ("Выражение"). 23. Используйте Expression Builder ("Построитель выражений"), чтобы присвоить выражению текстового поля следующее значение: =Parameters!SalesQuery.Value24. Просмотрите сформированный запрос. Он будет показан в текстовом поле, как на экране 3.
25. Убедитесь в правильности синтаксиса сформированного запроса. Он должен быть следующим:
26. На вкладке предварительного просмотра измените значения параметров и повторно запустите отчет, чтобы увидеть последствия сформированного запроса. Важно отметить, что значения параметров в этом примере не проверены и могут содержать ошибки или встроенный программный код. Решение необходимо дополнить мерами защиты, прежде чем внедрять его в производство. Но об этом позже. Использование сформированного запроса для создания набора данных отчетаЕсли отчет формирует корректные запросы, то следующая задача - скопировать сценарий SQL и использовать его для создания начального запроса к набору данных. Выполните перечисленные ниже действия.
Функционально отчет завершен, но в нем есть ряд уязвимых мест. Разработчики служб SSRS предусмотрели несколько мер безопасности, но не играйте с огнем. В этот пример не нужно вносить изменений, а при внедрении в производство рекомендуется изучить материал раздела "Безопасность - в первую очередь", чтобы исключить возможность атаки путем внедрения кода SQL. Динамическое скрытие столбца FreightВозможность выборочно показывать или скрывать различные столбцы - первый шаг к тому, чтобы возложить на один отчет функции многих отчетов. Чтобы показать, как это делается, динамически скроем столбец Freight. Выполните следующие действия в конструкторе отчетов. 1. Щелкните ячейку таблицы, чтобы показать маркеры выделения столбца и строки. 2. Щелкните правой кнопкой мыши серый заголовок для столбца Freight и выберите пункт Column Visibility. 3. В диалоговом окне Column Visibility выберите переключатель с меткой Show or hide based on an expression ("Показать или скрыть в зависимости от выражения"). 4. Откройте диалоговое окно Expression Builder ("Построитель выражений") и введите выражение =(Fields!Freight.IsMissing).5. Нажмите кнопки OK, чтобы сохранить и закрыть диалоговые окна Expression Builder и Column Visibility ("Видимость столбца"). Изменение набора данных для использования сформированного кодаНеобходимо выполнить небольшую реорганизацию, изменив способ подготовки запроса. Выполните следующие действия. 1. На панели Report Data дважды щелкните ReportDatadataset. 2. В диалоговом окне Dataset Properties ("Свойства набора данных") нажмите кнопку Expression ("Выражение") (fx) рядом с полем Query ("Запрос"). 3. В диалоговом окне Expression Builder замените текст команды ссылкой на параметр SalesQuery: =Parameters!SalesQuery.Value4. Нажмите кнопку OK, чтобы сохранить и закрыть диалоговые окна Expression Builder и Dataset Properties. 5. Убедитесь, что параметр SalesQuery имеет значение Hidden. 6. Измените свойство Hidden, чтобы скрыть текстовое поле, в котором показано значение параметра SalesQuery. 7. Просмотрите отчет и поэкспериментируйте с различными значениями параметра. Образец отчета показан на экране 4.
Отладка запросаТакое решение, как правило, требуется отладить. Полезно составить короткий список пунктов отладки и выполнять их каждый раз, когда необходимо изменить проект или программный код. Если вас не устраивают полученные результаты, следует сначала нажать кнопку Refresh ("Обновить") на инструментальной панели отчетов, чтобы очистить кэш выполнения и перезапустить запрос. SSRS устроен так, что прошлые результаты сохраняются в кэше. Если правильных результатов получить все же не удается или параметры работают некорректно, то, скорее всего, набор данных не использует сформированный код. Для отладки отмените скрытие параметра SalesQuery и покажите текстовое поле. Выполняя отчет, проверьте значения, чтобы убедиться в правильности синтаксиса запроса. Если он неверный, значит, ошибка в программном коде. Если параметр правильно сформировал синтаксис запроса в текстовом поле, но не изменил вывод отчета, то остается только проверить выражение, использованное для сопоставления набора данных параметру. Безопасность - в первую очередьВсегда, когда запрос формируется из данных пользовательского ввода, необходимо обратить внимание на безопасность. Внедрение кода SQL - метод, используемый хакерами для несанкционированного доступа к данным и компонентам сервера базы данных. Службы SSRS менее уязвимы для атак путем внедрения кода SQL благодаря ряду встроенных мер безопасности, однако важно проявлять осторожность при использовании параметров, открытых для таких запросов. Необходимо убедиться, что учетная запись пользователя, применяемая источником данных, имеет доступ только для чтения и только к данным, необходимым для отчета. Также нужно быть аккуратным с параметрами с текстовым типом данных. Числовые, логические данные и даты обычно не представляют угрозы, так как вместо их значений нельзя подставить сценарий. При возможности скрывайте параметры с текстовыми данными. Для параметров, которые должны быть видимыми и требуют текстовых значений, используйте специализированную функцию, чтобы проверять текстовые значения. Разрешайте только определенные значения и отсекайте строки, ограничивая их максимальную длину. Используйте политики исключений безопасностиЛюбой пользовательский код сборки, который управляет строками, форматированием или подготовкой динамических запросов, как в данном примере, будет прекрасно выполняться и не потребует специальной настройки. Если этим ограничивается назначение сборки, то можно начинать действовать. Если же сборка содержит ссылки на пространства имен, требующие дополнительных разрешений, для доступа к внешним ресурсам, таким как файловая система, то по умолчанию сборка не будет доверенной. Таким образом, необходимо настроить явные доверительные отношения между сервером отчетов и сборкой. Сделать это несложно, но необходимо строго следовать инструкциям. Реализация политики исключений безопасности - задача управления, которую требуется тщательно продумать перед внедрением в производство. Прежде чем просить администратора корпоративных серверов о реализации политик исключений безопасности для компонента, следует четко разъяснить, для каких целей может использоваться компонент и почему исключение - обязательная часть решения. В статьях MSDN "Code Access Security in Reporting Services" и "Using Reporting Services Security Policy Files" подробно объясняются критерии и показано, как создать необходимые группы кода и именованные наборы разрешений. Полезный методНастраиваемые сборки можно использовать в отчетах для различных целей. Например, централизованно управляемый компонент поможет единообразно применять стили ко всем отчетам. Изменив одно значение базы данных или выбирая параметр, можно изменить логотип компании или применять различные стили, рамки и шрифты к каждому отчету. В статье показан один из многих возможных методов. Я надеюсь, что читатели видят потенциал этого подхода и смогут с успехом применять его для разработчиков отчетов, а главное - для пользователей из сферы бизнеса и акционеров компаний, доверивших вам свое благосостояние.
USE AdventureWorksDW2012 GO CREATE view dbo.vSalesByTimeAndTerritory AS SELECT top 100 percent d.CalendarYear , d.CalendarQuarter , d.MonthNumberOfYear , d.EnglishMonthName MonthName , st.SalesTerritoryGroup , st.SalesTerritoryRegion , st.SalesTerritoryCountry , sum(s.SalesAmount) SalesAmt , sum(s.OrderQuantity) OrderQty , sum(s.Freight) Freight FROM FactResellerSales s inner join DimDate d on s.OrderDateKey = d.DateKey inner join DimSalesTerritory st on s.SalesTerritoryKey = st.SalesTerritoryKey GROUP BY d.CalendarYear , d.CalendarQuarter , d.MonthNumberOfYear , d.EnglishMonthName , st.SalesTerritoryGroup , st.SalesTerritoryRegion , st.SalesTerritoryCountry ORDER BY d.CalendarYear , d.CalendarQuarter , d.MonthNumberOfYear , st.SalesTerritoryGroup , st.SalesTerritoryRegion , st.SalesTerritoryCountry GO
' Note that the line continuations are included only ' so that the code fits on the page. Public Function SalesQuery(ByVal YearFrom As Integer, ByVal YearTo As Integer, ByVal Country As String, ByVal MaxRows As Integer, ByVal IncludeFrieght As Boolean) As String Dim sSelect As String = "SELECT TOP" & MaxRows.ToString() _ & "CalendarYear, CalendarQuarter, MonthNumberOfYear," _ & "MonthName, SalesTerritoryCountry," _ & "SUM(SalesAmt) AS SalesAmt, SUM(OrderQty) AS OrderQty" Dim sFrom As String = "FROM vSalesByTimeAndTerritory" If IncludeFrieght Then sSelect &= ", SUM(Freight) AS Freight" Dim sWhere As String = "" If YearFrom <> -1 Then sWhere ="(CalendarYear BETWEEN "_ & YearFrom.ToString() _ &" AND "& YearTo.ToString() &") " If Country <>"All"Then If sWhere =""Then sWhere &="SalesTerritoryCountry = '"& Country &"' " Else sWhere &=" AND SalesTerritoryCountry = '"_ & Country &"' " End If End If If sWhere <>""Then sWhere ="WHERE "& sWhere Dim sGroupBy As String ="GROUP BY CalendarYear, "_ &"CalendarQuarter, MonthNumberOfYear, MonthName, "_ &"SalesTerritoryCountry" Return sSelect & vbCrLf & sFrom & vbCrLf & sWhere _ & vbCrLf & sGroupBy End Function |