SharePoint 2010. Отчеты MS SSRS без сервера отчетов
Сегодняшний пост будет посвящен использованию отчетов, созданных с помощью Microsoft SQL Server Reporting Services (MS SSSRS), в портальных решениях под управлением MS SharePoint 2010. Более подробно я расскажу о вариантах работы с отчетами без сервера отчетов.

Режим работы сервера отчетов
Начну с небольшого вступления, описав режимы работы сервера отчетов:
- стандартный - сервер отчетов работает сам по себе. В SharePoint эти отчеты можно просматривать с помощью соответствующих веб-частей. Учетные данные для доступа к отчетам задаются в центре администрирования;
- интеграция с SharePoint 2010 - сервер отчетов хранит все свои данные в библиотеках документов SharePoint;
В случае интеграции сервера отчетов с SharePoint придется включить сервер, на котором работает MS SSRS в ферму SharePoint, что потребует наличия установленного SharePoint. При этом не стоит забывать, что в ферму SharePoint можно включать ТОЛЬКО ОДИНАКОВЫЕ РЕДАКЦИИ SHAREPOINT. Подробнее можно почитать на MSDN.
SQL Server 2012
С выходом SQL Server 2012 режим интеграции сервера отчетов с SharePoint кардинально изменился. Теперь сервер отчетов в режиме интеграции работает как общая служба-приложение. Следовательно, поддается конфигурации через центр администрирования SharePoint, масштабированию, работе с Claim-based аутентификации и прочему. Подробнее здесь.

Отчеты без сервера отчетов
Отчеты можно использовать и при отсутствии сервера отчетов. Здесь есть два варианта.
Report Builder
Первый способ заключается в использовании построителя отчетов (Report Builder) для создания отчетов. В дальнейшем эти отчеты можно хранить в библиотеке документов на портале и там же просматривать. Вот небольшой step-by-step:
Запускаем Report Builder и создаем новый источник данных. В типе соединения мы выбираем "Microsoft SharePoint List". Строкой подключение в этом случае будет просто URL-адрес сайта, из списков которого мы будем получать данные:

Источников данных в одном отчете может быть много. Теперь нам необходимо создать набор данных (DataSet). Здесь Report Builder более чем дружелюбен: он отображает информацию о списках и библиотеках документов на указанном в источнике данных сайте. Нам достаточно просто отметить те поля, которые нас интересуют:

Report Builder по умолчанию выбирает все данные без учета их типа содержимого. Чтобы в отчет не попали папки придется наложить фильтр.
Фильтр - это ерунда по сравнению с вот такой особенностью Report Builder'а: Данные можно выбирать только из корневой папки списка/библиотеки. Если очень надо обойти это ограничение, то можно работать со списками SharePoint через веб-сервисы, куда передавать CAML-запрос с фильтром по папке. В Report Builder'е обойти это не удастся. Я был уверен, что в новой версии построителя Microsoft исправит этот недочет, но я ошибался. Я пользовался версией 11.0.2100.60 (SQL Server 2012):

Недостатков в таком случае много. Например подписка на отчет и вызов редактора отчета через контекстное меню элемента в таком случае не работают. К тому же при просмотре отчета SharePoint попросит указать учетные записи для каждого источника данных (DataSource).
ReportViewer и объектная модель
Второй способ заключается в использовании ReportViewer'а - контрола, позволяющего отображать отчеты на формах. Так как мы будем отображать отчеты без использования сервера отчетов, то использовать мы будем LocalReport, а в качестве источника данных будет выступать объектная модель.
Для начала добавим новый отчет в наш проект

Далее создадим новые источник данных (DataSource) и набор данных (DataSet). В разделе Data Source нажимаем кнопку New... для создания нового источника данных

В качестве типа источника данных выбираем Object

Указываем класс, который будет выступать источником данных. В моем примере я выбрал класс Employee

Теперь наш отчет обеспечен данными

Поля из модели данных можно использовать при построение самого отчета

Мой демонстрационный отчет будет отображать список сотрудников, сгруппированный по отделам

Отчет готов. Теперь его надо отобразить на странице. Можно делать это программно:
- // Путь к файлу отчета
- 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>
Вот и все на сегодня. Надеюсь пригодится.