Так подумать, действительно у MS до сих пор не было своего собственного продукта для создания reports. Тем более странно, что CrystalReports давно стал синонимом тупика. Настолькo тупика, что ссли бы меня спросили месяца 3 тому, как быстрее и проще сделать reports для Web, я бы почти наверняка сказал бы - обычный asp.net. И в выборе data provider ты не ограничен, и layout - весь в твоих руках. Время разработки осталось? Кодируйте быстрее!
Но вот появились
Reporting Services - жизнь изменилась. Относительно data providers, то наконец, разрабочик report стал полноценным членом ассоциации Web developers: ado.net и для него стал родным и близким.Кстати заметим, что ADO.NET , конечно, включает и OLAP для Web based reports (aх, Crystal, ну впрямь тупик).
Layout - вообще для reports не совсем адекватно воспринимается Web-разработчиком. Report layout - это не совсем таблица,вернее разработчикам Reporting Services представляется, что в случае с reports, он проще и нет необходимости давать разработчику возмость доступа к коду самой страницы. Только design. Кодом report называют новый, XML-based язык - RDL: Report Definition Language. Как он обрабатывается в конечном итоге показываемую старницу - "тайна" одной из главных компонент RS - Report Processor. Он, в свою очередь, - часть Report Server: набор core-components, которые как за первичную обработку RDL, так и за практически всю остальную функциаональность продукта по части scheduling, delivery, delivery extension, rendering extension, data processing-extension etc.
Все эти компоненты вместе образуют "слой", называемым в документации
Report Server. Его задача - получив RDL на входе, обработать его (с помощью толькo что перечисленных служб) в HTML или , в общем, в то, что будет указано в Rendering Extension.
Еще один "слой" продукта образует
Report Manager. Он отвечает за конфигурацию reports: subscriptions, report parameters, secured access etc.
RS Components Access
Report Manger - это Web application,по умолчанию расположенная в ..Program FilesMicrosoft SQL ServerMSSQLReporting ServicesReport Manager. Она предоставляет UI , с помощью которого и осуществляются соответствующие операции конфигурирования. Default installation makes it with web alias - Reports, т.е. доступ к Report Manager, обычно, такой:
http://server/Reports
(Кстати, по каким-то непонятным причинам, в Report Manager Web application отсутствует default.xxx страница. Вместо этого, default installation конфигурирует IIS на home.aspx по умолчанию.)
Report Server - это Web-service с достаточно широким набором методов. Доступ к нему, может быть осуществлен или напрямую через URL, или через Web-reference. Во втором случае, это достаточно тривиально: методы, как методы, параметры не сложные. Как и ожидалось, используется SOAP. Вызывайте, но только никакого метода, похожего на GenerateReport(), там не найдется. А если бы и нашелся? В конце концов, Report Server должен как-то отобразить report в окне browser. SOAP method вряд-ли подходит для этого. Представьте себе это сами. Как именно вызвать такой SOAP method? Будь я на месте архитекторов Reporting Services, я бы предложил еще одну, дополнительную Web application, которая будет заниматься ислючительно этим - генерировать HTML из report definition (RDL). Такая аппликация существует. И называется она - (отгадайте, догадливые) - Report Processor. Разделение обязанностей между ними очень простое: Report Server отвечает за операции с reports в базе данных, все его методы обращаются к базе за различной информацией, уже ассоциированной с report, или добавляя к report разные элементы, properties.
Report Processor получает имя report, с одной стороны, и (за кулисами - из базы) его properties, с другой, и на основе этих двух, генерирует HTML. Доступ к Report Processor - это и есть известный в документации - URL access.
Выглядит это так:
http://server/reportserver?[/reportpath]&prefix:param=value[@prefix:param=value]...n]
Например : http://flex/reportserver?/everest/agencyReport
Пусть вас не смущает тот же адрес - и для Report Server, и для Report Processor - по сути вы запрашиваете ту самую, дополнительную Web application - Report Processor.
Все бы ничего, но у report есть параметры. Если бы Report Processor имел только URL access, запрос того же самого report с другими параметрами сводился бы каждый раз к изменeнию URL. Это не удобно (хотя тоже возможно)- полный refresh, как ни как. Да и что делать в случае, если report нужно разместить hosted в другой странице? Среди samples, поставляемых с Repoting Services, есть ответ на эту "проблему" - Report Viewer. Не совсем, правда, понятно, почему он удостоился только места в samples, когда внешний вид Report Preview из MS Studio Report Designer от него не отличим, а чего он делает внутри, можно только предположить, что именно его-то разработчики и вынесли в samples.
Но, как бы там не было, он отлично размещается в Toolbox и готов для hosting в любой вашей ASP.NET странице.
Snapshots
Возможность запустить report как snapshot - просто бесценна для случаев, когда произведенный report имеет ценность и после того, как данные в базе изменились. Любой
report может быть отконфигурировн для запуска, как snapshot через Report Manager. Причем, имеется 2 возможности: создать snapshot "единоразово" в момент конфигурации, или создавать его через фиксированные интервалы времени - schedule. Если reprort имеет параметры, то они быть зафиксированы в момент создания snapshot.
Если запрос на обычное исполнение report выглядит, например, как:
http://flex/reportserver?/everest/agencyReport&agencyID=444&Quater=Q1
то запрос на исполнение report, как snapshot так:
http://flex/reportserver?/everest/agencyReport&rs:Snapshot=2004-09-25T22:48:51
В этом случае, как видите, никакие параметры передавать уже нельзя - данные, которые показываются в report фиксированы и хранятся вместе с report, а не вычисляются на основе dataset.
Осталось выяснить, каким же образом програмно можно задать snapshot с параметрами?
Сначала оставим параметры в стороне. Просто создать report можно с помощью
ReportingService.CreateReportHistorySnapshot() Этот метод возвращает date and timestamp. Он уникален (almost) и может служить для однозначного нахождения созданного snapshot.
Допустим, ваш набор параметров
agencyID=444
Quater=Q1
tolerance=2
и snapshot создан 26 сентября в 15:00.00. Тогда его timestamp будет, понятно, 2004-09-26T15:00:00 и запрошен такой snapshot может быть как, например:
http://flex/reportserver?/Everest/agencyReport&rs:Snapshot=2004-09-2004-09-26T15:00:00.
Теперь запомним это сочетание параметров и timestamp отдельно, в какой-нибудь таблице (а может и hash) и разрешим (а лучше бы это сделать сразу) "history to be created manually" (см. ReportingService.SetReportHistoryOptions() ). Задаем новые параметры, и снова вызываем ReportingService.CreateReportHistorySnapshot().Новый timestamp - новая запись в таблицу сочетаний.
По-другому, snapshot может быть запрошен и так:
http://flex/ReportServer?Everest/agencyReport&rs:Command=Render&rs:historyID=2004-09-26T15:00:00Кстати, чисто для справки, такой формат времени называется ISO 8601.
И еще кстати, параметры сохраненные для snapshot, могут быть узнаны и без таблицы сочетаний. Для этого служит метод GetReportParameters(). В случае, если он вызывается для snapshot, ему нужен ForRendering=true.
Вот целая статья на тему об использовании этого метода
http://odetocode.com/Articles/123.aspx
А теперь кое-что из недокументированных возможностей. RS DB (так и называется - ReportServer) имеет таблицу SnapshotData, в которой каждый созданный snapshot уже ассоциирован со своими параметрами. Колонка, содержащая все об этих параметрах называется EffectiveParams.Тип - ntext(256).А содержит она по сути XML. Безо всяких namespace и других наворотов - чистый, наивный XML. Имеющий руки, да подлключится к этой базе, да не постыдится использовать старый, добрый DataReader из ADO.NET иже со славным XML процессором, дабы не утруждать себя еще сочетаниями всякими. :)
Ну а узнать, что для report создан snapshot, можно тоже напрямую - из главной таблицы - Catalog. См. поле SnapshotDataID. Вот и query:
select Path, [Name], Catalog.SnapshotDataID
from Catalog left outer join SnapshotData
on Catalog.SnapshotDataID = SnapshotData.SnapshotDataID
where Catalog.SnapshotDataID is not null
И раз уже зашла речь о недокументированных возможностях, то касательно snapshot, так и хочется спросить - а где же хранится собственно data для snapshot? В таблице ChunkData. Почти такой же left join, как двумя строчками выше, вернет вам всю data. Правда, как image. Когда-нибудь, будет интересно на них и взглянуть.