SharePoint 2010. Отчеты MS SSRS без сервера отчетов
Сегодняшний пост будет посвящен использованию отчетов, созданных с помощью Microsoft SQL Server Reporting Services (MS SSSRS), в портальных решениях под управлением MS SharePoint 2010. Более подробно я расскажу о вариантах работы с отчетами без сервера отчетов.
![](http://blog.vitalyzhukov.ru/imagelibrary/MSReportingNote.png)
Режим работы сервера отчетов
Начну с небольшого вступления, описав режимы работы сервера отчетов:
- стандартный - сервер отчетов работает сам по себе. В SharePoint эти отчеты можно просматривать с помощью соответствующих веб-частей. Учетные данные для доступа к отчетам задаются в центре администрирования;
- интеграция с SharePoint 2010 - сервер отчетов хранит все свои данные в библиотеках документов SharePoint;
В случае интеграции сервера отчетов с SharePoint придется включить сервер, на котором работает MS SSRS в ферму SharePoint, что потребует наличия установленного SharePoint. При этом не стоит забывать, что в ферму SharePoint можно включать ТОЛЬКО ОДИНАКОВЫЕ РЕДАКЦИИ SHAREPOINT. Подробнее можно почитать на MSDN.
SQL Server 2012
С выходом SQL Server 2012 режим интеграции сервера отчетов с SharePoint кардинально изменился. Теперь сервер отчетов в режиме интеграции работает как общая служба-приложение. Следовательно, поддается конфигурации через центр администрирования SharePoint, масштабированию, работе с Claim-based аутентификации и прочему. Подробнее здесь.
![](/imagelibrary/SP2010_RS2012_01.png)
Отчеты без сервера отчетов
Отчеты можно использовать и при отсутствии сервера отчетов. Здесь есть два варианта.
Report Builder
Первый способ заключается в использовании построителя отчетов (Report Builder) для создания отчетов. В дальнейшем эти отчеты можно хранить в библиотеке документов на портале и там же просматривать. Вот небольшой step-by-step:
Запускаем Report Builder и создаем новый источник данных. В типе соединения мы выбираем "Microsoft SharePoint List". Строкой подключение в этом случае будет просто URL-адрес сайта, из списков которого мы будем получать данные:
![](http://blog.vitalyzhukov.ru/ImageLibrary/ReportBuilder_NewDataSource_thumb.png)
Источников данных в одном отчете может быть много. Теперь нам необходимо создать набор данных (DataSet). Здесь Report Builder более чем дружелюбен: он отображает информацию о списках и библиотеках документов на указанном в источнике данных сайте. Нам достаточно просто отметить те поля, которые нас интересуют:
![](http://blog.vitalyzhukov.ru/ImageLibrary/ReportBuilder_NewDataSet_thumb.png)
Report Builder по умолчанию выбирает все данные без учета их типа содержимого. Чтобы в отчет не попали папки придется наложить фильтр.
Фильтр - это ерунда по сравнению с вот такой особенностью Report Builder'а: Данные можно выбирать только из корневой папки списка/библиотеки. Если очень надо обойти это ограничение, то можно работать со списками SharePoint через веб-сервисы, куда передавать CAML-запрос с фильтром по папке. В Report Builder'е обойти это не удастся. Я был уверен, что в новой версии построителя Microsoft исправит этот недочет, но я ошибался. Я пользовался версией 11.0.2100.60 (SQL Server 2012):
![SQL Server 2012 Reporting Builder 3.0](http://blog.vitalyzhukov.ru/ImageLibrary/ReportBuilder_About.png)
Недостатков в таком случае много. Например подписка на отчет и вызов редактора отчета через контекстное меню элемента в таком случае не работают. К тому же при просмотре отчета SharePoint попросит указать учетные записи для каждого источника данных (DataSource).
ReportViewer и объектная модель
Второй способ заключается в использовании ReportViewer'а - контрола, позволяющего отображать отчеты на формах. Так как мы будем отображать отчеты без использования сервера отчетов, то использовать мы будем LocalReport, а в качестве источника данных будет выступать объектная модель.
Для начала добавим новый отчет в наш проект
![](/imagelibrary/LocalReport_SolutionExplorer.png)
Далее создадим новые источник данных (DataSource) и набор данных (DataSet). В разделе Data Source нажимаем кнопку New... для создания нового источника данных
![](http://blog.vitalyzhukov.ru/ImageLibrary/LocalReport_DataSetSelection_thumb.png)
В качестве типа источника данных выбираем Object
![](http://blog.vitalyzhukov.ru/ImageLibrary/LocalReport_NewDataSource_SelectType_thumb.png)
Указываем класс, который будет выступать источником данных. В моем примере я выбрал класс Employee
![](http://blog.vitalyzhukov.ru/ImageLibrary/LocalReport_SelectModel_thumb.png)
Теперь наш отчет обеспечен данными
![](http://blog.vitalyzhukov.ru/ImageLibrary/LocalReport_SelectedDataSet_thumb.png)
Поля из модели данных можно использовать при построение самого отчета
![](http://blog.vitalyzhukov.ru/ImageLibrary/LocalReport_ReportData.png)
Мой демонстрационный отчет будет отображать список сотрудников, сгруппированный по отделам
![](http://blog.vitalyzhukov.ru/ImageLibrary/LocalReport_ReportDesigner_thumb.png)
Отчет готов. Теперь его надо отобразить на странице. Можно делать это программно:
- // Путь к файлу отчета
- var reportPath = SPUtility.GetGenericSetupPath(
- @"template\layouts\ZhukBlogLinqExamples\Reports\") + @"\AllEmployees.rdlc";
- // Содаем новый экземпляр ReportViewer'а
- var reportViewer = new ReportViewer
- {
- Width = new Unit("100%")
- };
- reportViewer.LocalReport.DisplayName = "Employees";
- reportViewer.LocalReport.ReportPath = reportPath;
-
- // Данные для отчета будем получать с помощью репозитория
- var repository = new EmployeeRepository(true);
- var items = repository.GetEntityCollection();
- // Создаем новый ситочник данных
- var ds = new ReportDataSource("EmployeeDataSet", items);
- reportViewer.LocalReport.DataSources.Add(ds);
- // Добавляем ReportViewer на страницу
- Controls.Add(reportViewer);
Декларативно это будет выглядеть примерно вот так:
- <asp:ObjectDataSource ID="ReportDS" runat="server"
- TypeName="EmployeeRepository, $SharePoint.Project.AssemblyFullName$"
- SelectMethod="GetEntityCollection">
- </asp:ObjectDataSource>
- <rsweb:ReportViewer ID="EmpReportViewer" runat="server" Width="100%" Height="100%">
- <LocalReport DisplayName="Список сотрудников" ReportPath="[...]\AllEmployees.rdlc">
- <DataSources>
- <rsweb:ReportDataSource DataSourceId="ReportDS" Name="EmployeeDataSet" />
- </DataSources>
- </LocalReport>
- </rsweb:ReportViewer>
Генерация документа из отчета
Чтобы программно получить отчет, экспортированный в какой-либо формат, поддерживаемый ReportViewer'ом, можно использовать вот такой код:
- var report = reportViewer.LocalReport;
- var bytes = report.Render("IMAGE");
Метод Render здесь возвращает массив байтов, что крайне удобно для дальнейшего сохранения результата в библиотеке документов SharePoint. В параметре указывается желаемый формат данных. Получить список этих форматов можно, вызвав метод LocalReport.ListRenderingExtensions(). В моем случае были доступны следующие форматы:
- Excel - Microsoft Excel 2003;
- EXCELOPNEXML - Microsoft Excel 2007/2010;
- IMAGE - TIFF-файл;
- PDF - Adobe Portable Document Format;
- Word - Mcirosoft Word;
- WORDOPENXML - Microsoft Word 2007/2010;
Производительность
При построение отчетов MS SQL Reporting с использованием объектной модели в качестве источника данных надо помнить о том, что перед рендерингом очередного элемента произойдет неявное получение значений ВСЕХ свойств объекта. Т.е. lazy-load свойства со сложной логикой внутри в отчетах использовать надо очень аккуратно. Чтобы убрать "лишние" свойства из набора данных, придется открыть файл отчета в XML-редакторе и отредактировать раздел Fields. Содержимое файла-отчета выглядит примерно следующим образом (частично):
- <?xml version="1.0" encoding="utf-8"?>
- <Report
- xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner"
- xmlns="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition">
- <DataSources>
- <DataSource Name="ZhukBlogLinqExamplesModel">
- <ConnectionProperties>
- <DataProvider>System.Data.DataSet</DataProvider>
- <ConnectString>/* Local Connection */</ConnectString>
- </ConnectionProperties>
- <rd:DataSourceID>7a010698-afb4-4648-ac51-2701e2a975c4</rd:DataSourceID>
- </DataSource>
- </DataSources>
- <DataSets>
- <DataSet Name="EmployeeDataSet">
- <Fields>
- <Field Name="AccessLevel">
- <DataField>AccessLevel</DataField>
- <rd:TypeName>System.Int32</rd:TypeName>
- </Field>
- <!-- ... -->
- </Fields>
- <Query>
- <DataSourceName>ZhukBlogLinqExamplesModel</DataSourceName>
- <CommandText>/* Local Query */</CommandText>
- </Query>
- <rd:DataSetInfo>
- <rd:DataSetName>ZhukBlogLinqExamples.Model</rd:DataSetName>
- <rd:TableName>Employee</rd:TableName>
- <rd:ObjectDataSourceType>ZhukBlogLinqExamples.Model.Employee,...
- </rd:ObjectDataSourceType>
- </rd:DataSetInfo>
- </DataSet>
- </DataSets>
- <!-- ... -->
- </Report>
Вот и все на сегодня. Надеюсь пригодится.