SQL Server 2000 Reporting Services. (Part 2)
Введение.
Довольно много времени прошло с момента написания первой статьи. Я планировал написать вторую
статью быстрее, но жизнь внесла определенные коррективы. Но, как говорится, лучше поздно, чем
никогда. Сразу хочется поблагодарить Вас за отзывы и замечания по первой статье. Мне приятно, что
мой материал оставил приятное впечатление. Это вдохновляет на дальнейшую работу.
О чем будем читать?
В данной статье мне хотелось бы глубже погрузиться в мир RS (Reporting Services) и
коснуться некоторых моментов управления «изнутри», т.е. при помощи Web методов.
Что будем писать?
В качестве примера, я решил реализовать небольшой RS Explorer, который мог бы
построить нам дерево отчетов и поддерживал функции рендеринга отчетов во
встроенном окне при помощи com компонента: Microsoft Web Browser. Кроме того, я
добавлю еще функцию экспорта в форматы, поддерживаемые RS без отображения
самого отчета. Так же в этом примере мы увидим пример использования параметров
отчетов по умолчанию, и изменения их до вызова отчета.
Реализация.
Начну с того, что при реализации данного примера я использовал компоненты стороннего
производителя. Зачем я это сделал? Во-первых, хочется разнообразия, во-вторых, я
привык к их архитектуре, так как давно ими пользуюсь. Можно добавить в третьих, хочется
разнообразия. Кроме того, высока вероятность дальнейшего развития этого проекта.
В любом случае, как полный исходный код проекта, так и скомпилированный пример, будут
доступны на нашем портале для загрузки. Исходный код Вы сможете легко адаптировать к
любым Вашим компонентам.
Цели и задачи определены. Приступим.
В дизайнере «накидываем» компонентов и строим будущий каркас приложения. Здесь все
стандартно. Справа дерево. Под деревом расположилось маленькое окошко Description, куда
мы будем выводить различную, сопутствующую информацию. Слева окно com компонента
Microsoft Windows Browser.
Для тех, кто не знает, как вытащить компонент Microsoft Web Browser сделаю небольшой экскурс. Открываем окошко ToolBox. Выбираем какой-нибудь раздел в Visual Studio .Net 2003 (Я выбрал Components) и, нажав правую клавишу мыши, выбираем пункт меню “Add/Remove Items”. После чего у Вас откроется окошко с возможностью выбора компонент для дизайнера. После открытия окошка переходим на закладку COM Components и ищем там Microsoft Web Browser. Это стандартный COM объект – он не нуждается в отдельной установке. После нажатия Ok Вы увидите, как появиться компонент в ToolBox.
Хотелось бы отметить, что в VS .Net 2005 этот компонент, уже в виде .Net компонента, находится в окне ToolBox.
Будем считать, что дизайн будущего приложения уже построен. Теперь нам нужно заполнение дерева.
Не забываем определить Credentials и Url при инициализации нашей ссылки на экземпляр объекта ReportingService:
private void FormMain_Load(object sender, EventArgs e)
{
if(!DesignMode)
{
...
rs = new ReportingService();
rs.Credentials = CredentialCache.DefaultCredentials;
this.rs.Url = this.barItemURL.EditValue + "/ReportService.asmx";
}
}
Так как наши компоненты поддерживают Binding для объектов типа DataSet, я предлагаю воспользоваться этим замечательным качеством и создать простую, универсальную таблицу для возможности использования ее в будущем для навигации по дереву.
Следуя логике повторного использования кода, мы берем метод, который был предложен в первой статье для получения списка отчетов и, внеся некоторые модификации, получаем весь список CatalogItems. В данном методе реализовано заполнение таблицы по методу Id-ParentId для построения компонентом дерева. Как это сделать? Есть множество способов и я думаю, что реализация не вызовет у Вас каких либо затруднений. Я использовал свойство CatalogItem – Path (путь к объекту на RS). Результат «вылился» в такую группу методов.
Основной метод, заполняющий таблицу из DataSet.
#region [ BuildTree ]
public void BuildTree()
{
this.Cursor = Cursors.WaitCursor;
CatalogItem[] items;
ReportParameter[] parameters = null;
try
{
items = rs.ListChildren("/", true); // получаем список объектов по данному пути
this.ds.Reports.Clear();
foreach(CatalogItem i in items)
{
RServDS.ReportsRow drNew = this.ds.Reports.NewReportsRow();
drNew.ID = i.Name;
if(i.Path.LastIndexOf('/') > 0)
drNew.ParentID = i.Path.Substring( i.Path.Substring(0, i.Path.LastIndexOf('/')).LastIndexOf('/') + 1, i.Path.LastIndexOf('/') - 1);
else
drNew.SetParentIDNull();
switch(i.Type)
{
case ItemTypeEnum.DataSource:
drNew.ImageIndex = 0;
break;
case ItemTypeEnum.Folder:
drNew.ImageIndex = 1;
break;
case ItemTypeEnum.Report:
parameters = GetListParameters(i.Path);
drNew.ImageIndex = 2;
break;
case ItemTypeEnum.LinkedReport:
drNew.ImageIndex = 3;
break;
case ItemTypeEnum.Resource:
drNew.ImageIndex = 4;
break;
case ItemTypeEnum.Unknown:
drNew.ImageIndex = 5;
break;
default :
break;
}
drNew.Description = i.Description;
drNew.Name = i.Name;
drNew.Path = i.Path;
this.ds.Reports.AddReportsRow(drNew);
AddParametrToParent(parameters, drNew);
}
}
catch (Exception ex)
{
this.Cursor = Cursors.Default;
MessageBox.Show(ex.Message);
}
this.Cursor = Cursors.Default;
}
#endregion
Метод, получающий список параметров для конкретного отчета:
#region [ GetListParameters ]
public ReportParameter[] GetListParameters(string sReport)
{
return rs.GetReportParameters(sReport, null, false, null, null);
}
#endregion
Метод, дополняющий записи в таблицу из DataSet для параметров:
#region [ AddParametrToParent ]
private void AddParametrToParent(ReportParameter[] parameters, RServDS.ReportsRow drNew)
{
if(parameters != null && parameters.Length > 0)
{
foreach(ReportParameter rp in parameters)
{
RServDS.ReportsRow drParam = this.ds.Reports.NewReportsRow();
drParam.ID = rp.Name;
drParam.ParentID = drNew.ID;
drParam.ImageIndex = 6;
drParam.Name = rp.Name;
drParam.Path = (rp.DefaultValues != null && rp.DefaultValues.Length > 0) ? rp.DefaultValues[0] : String.Empty;
drParam.Description = (rp.DefaultValues != null && rp.DefaultValues.Length > 0) ? "Current value: " + rp.DefaultValues[0] : "No default value";
this.ds.Reports.AddReportsRow(drParam);
}
}
}
#endregion
Надо сказать, что в последнем методе я слукавил и в поле Path записал значения «по умолчанию» для параметров данного отчета. Мне не хотелось добавлять еще одно поле для этих значений, хотя по большому счету добавить было бы более правильно.
Теперь есть реализация заполнения дерева.
Немного теории.
Переходим к самому интересному, и, пожалуй, самому тонкому моменту: рендерингу отчетов. Здесь нужно сделать краткое отступление и сослаться на теорию.
Различают два основных способа взаимодействия с Reporting Services. Первый - это доступ через ссылку (URL Access) и второй, непосредственный, с использованием методов Reporting Service Web Service.
Основой первого метода, как Вы уже догадались, есть «правильно» построенная строка вызова URL. Базовый синтаксис этой строки выглядит следующим образом:
http://server/virtualroot?[/pathinfo]&[prefix:]param=value[&[prefix:]param=value]...n]
В котором имеем следующее:
server – здесь все просто: это имя сервера на котором собственно и предоставляется доступ к Reporting Services.
Virtualroot – виртуальная директория, настроенная Вами при установке RS. По умолчанию это директория ReportServer.
pathinfo – это название самого отчета, которое сконфигурировано в административном модуле.
[prefix:]param=value[&[prefix:]param=value]... – это набор все возможных параметров при генерации отчета. На них мы остановимся чуть подробнее, потому что как раз они и заслуживают больше всего нашего внимания.
Поработаем сначала с Internet Explorer. Для начала проверим папку с RS – в моем случае это: http://freelancer/ReportServer
Получаем картинку, представленную мной. Отличие будет в зависимости от созданной иерархии на Вашем сервере:
Если увидели подобную картинку, значит у Вас все настроено правильно. И RS работает нормально.
Для начала хочется посмотреть содержимое папки Catalog. Для этого формируем строку:
http://freelancer/ReportServer?%2fCatalog&rs:Command=ListChildren
rs:Command=ListChildren – как Вы успели заметить, эта команда отвечает за получения списка для директории.
Далее. Допустим, я хочу сразу же загрузить отчет под названием Product Catalog, находящийся в папке Catalog.
Для этого формируем строку с названием нужного нам отчета и даем команду генерации отчета: rs:Command=Render. При этом нам главное не забыть указать кроме папки, где лежит наш отчет еще и имя отчета. Получаем вот такую строку.
http://freelancer/ReportServer?%2fCatalog%2fProduct+Catalog&rs:Command=Render
В результате чего нам сформируется отчет:
Если же мы хотим просмотреть содержимое DataSource, мы формируем строку следующего формата:
http://freelancer/ReportServer?%2fAdventureWorks&rs:Command=GetDataSourceContents
Обратите внимание, здесь использована еще одна команда, выводящая непосредственно содержимое DataSource: rs:Command=GetDataSourceContents.
После выполнения данного запроса на выходе получим XML структуру следующего вида:
SQL
data source="(local)";persist security info=False;initial catalog=AdventureWorks2000
Integrated
True
Вы можете также самостоятельно поэкспериментировать с командами, переходя в Internet Explorer прямо по ссылкам.
На чем мне еще хотелось бы акцентировать Ваше внимание это на генерацию строки с параметрами для отчета. rs – это команды управления и просмотра отчетов, а rc – команды это как раз именно тот тип, через который Вы сможете управлять параметрами вызываемых отчетов.
Краткий обзор команд и параметров.
То, что может быть использовано с командой типа rs:
- Command. Это активные команды генерации.
- GetDataSourceContents – показывает содержимое DataSources (пример был приведен выше)
- GetResourceContents – то же что и GetDataSourceContents, но для ресурсов RS.
- ListChildren – показывает содержимое папок.
- Render – вызывает генерацию конкретного отчета.
- Format. Это группа команд для форматов генерации отчетов.
- Web форматы:
- HTML3.2
- HTML4.0
- MHTML
- Форматы для печати:
- IMAGE
- Форматы данных:
- EXCEL
- CSV
- XML
- Специальный формат
-
NULL - используется для NULL генерации отчета. Такой формат, например, удобно использовать для создания отчета в кеше.
- Web форматы:
- Snapshot. Используется для создания моментальных снимков.
Пример использования rc параметров может служить, например, «погашение» окна с параметрами для отчета. (&rc:Parameters=False)
http://freelancer/ReportServer?%2fSales+Report%2fProduct+Line+Sales&rs:Command=Render&rc:Parameters=False
или, например, генерации отчета в файл типа JPEG. (&rc:OutputFormat=JPEG):
http://freelancer/ReportServer?%2fSales+Report%2fProduct+Line+Sales&rs:Command=Render&rs:Format=IMAGE&rc:Parameters=False&rc:OutputFormat=JPEG
Кроме того, существуют еще префиксы dsu и dsp. Эти префиксы используются для парольного доступа к БД. Например, если мы хотим создать подключение к БД AdventureWorks2000, с именем guest и паролем guestPass, к нашему URL добавится следующая конструкция:
&dsu:AdventureWorks2000=guest&dsp:AdventureWorks2000=guestPass
Но здесь есть нюанс. Для работы с этими параметрами Вам нужно будет использовать криптование на базе SSL (Secure Socket Layers).
И последнее, на чем бы хотелось остановить Ваше внимание, это на вызове отчета с предустановленными параметрами. Здесь все просто:
http://freelancer/ReportServer?%2fSales+Report%2fProduct+Line+Sales&rs:Command=Render&StartDate=01/10/2005&EndDate=01/11/2005
Единственная сложность в том, что нужно заранее знать названия параметров.
Продолжение реализации.
Из этого небольшого экскурса теории Вы заметили, что основой генерации отчета при помощи URL является знание и правильная комбинация параметров и команд. В своем приложении я сформировал универсальный метод генерации строки для вызова отчета. Параметры хранятся в хеш массиве. Для начала определим метод установки параметров в этом массиве:
#region [ SetParameter ]
///
/// Add or remove url access string properties.
///
///
///
private void SetParameter(string name, string value)
{
try
{
// Remove if value is null or empty. Value is null of the property grid value
// is null or empty. Empty or null removes the property from the Hashtable.
if(value == null | value == String.Empty )
{
this._properties.Remove(name);
}
else
{
if(this._properties.ContainsKey(name))
{
// Change if key exists
this._properties[name] = value;
}
else
{
// Add if key does not exist
this._properties.Add(name, value);
}
}
// Build a new url string
this.BuildUrlString();
}
// Catch and handle a more specific exception in a propduction application.
catch(Exception ex)
{
// Sample throws the exception to the client
throw ex;
}
}
#endregion
Далее сам метод построения адреса генерации отчета:
#region [ BuildUrlString ]
///
/// Add URL access command for rendering a report and any
/// additional parameters.
///
public string BuildUrlString()
{
// Url: Full report url.
// ServerUrl: Server url such as http://localhost/reportserver
// ReportPath: Report path such as /SampleReports/Company Sales
string _url = this.barItemURL.EditValue + "?" +
this.GetCurrentRow().Path.Replace(' ', '+') +
"&rs:Command=Render" + this.EmumProperties(this._properties);
return _url;
}
#endregion
В этом методе используется метод для доступа к BindingContext. Это позволяет работать с текущей, выбранной строкой.
#region [ GetCurrentRow ]
public RServDS.ReportsRow GetCurrentRow()
{
try
{
// Опять парни из Developer Express нахомутали с биндингом для своего компонента. ПОтому приходися
// заменять нормальный алгоритм получения стандартной строки на "через одно место" :(
//return (RServDS.ReportsRow)((DataRowView)this.BindingContext[this.ds.Reports].Current).Row;
if(this.treeList.FocusedNode != null)
return (RServDS.ReportsRow)((DataRowView)this.treeList.GetDataRecordByNode(this.treeList.FocusedNode)).Row;
else
return null;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Ошибка!!!");
return null;
}
}
#endregion
Кроме того, необходим метод инициализации параметров:
#region [ EmumProperties ]
///
/// Enumerate Hashtable and create report server access specific string.
///
///
///
private string EmumProperties(Hashtable properties)
{
string paramsString = String.Empty;
// Enumerate properties and create report server specific string.
IDictionaryEnumerator customPropEnumerator = properties.GetEnumerator();
while ( customPropEnumerator.MoveNext() )
{
paramsString += "&"
+ customPropEnumerator.Key
+ "=" + customPropEnumerator.Value;
}
return paramsString;
}
#endregion
Все для генерации отчетов у нас готово. Мы можем убедиться в этом, поработав с получившимся интерфейсом. Я не буду сейчас останавливаться на, формах-спутниках, обработке нажатий мыши и т.д. – я думаю, что в коде Вы легко сможете разобраться. После двойного клика мышкой на отчет Product Catalog в папке Catalog результат должен выглядеть следующим образом:
Работа с WS Reporting Services
Выше мы рассмотрели работу с URL строкой для генерации отчетов. Сейчас я предлагаю сделать еще один эксперимент. Мы попробуем сгенерировать отчеты в любой допустимый для RS выходной формат без использования URL строки, а исключительно при помощи Web методов.
Для этого создадим универсальный метод для сохранения файла отчета:
#region [ SaveFile ]
public void SaveFile(string sReport, string sFormat, string sExt)
{
Stream myStream ;
BinaryWriter bw;
string encoding, mimeType;
Warning[] warnings;
string[] streamIds;
ParameterValue[] parametersUsed;
saveFileDialog.Filter = sExt;
saveFileDialog.FilterIndex = 1 ;
saveFileDialog.RestoreDirectory = true ;
if(saveFileDialog.ShowDialog() == DialogResult.OK)
{
if((myStream = saveFileDialog.OpenFile()) != null)
{
byte[] data = rs.Render(sReport, sFormat, null, null,
GetParametersValue(this._properties), null, null,
out encoding, out mimeType, out parametersUsed,
out warnings, out streamIds);
bw = new BinaryWriter(myStream);
bw.Write(data, 0, data.Length);
myStream.Close();
MessageBox.Show("File written to: " + saveFileDialog.FileName);
}
}
}
#endregion
Для него потребуется еще один метод генерирующий значения параметров из хеш массива.
#region [ GetParametersValue ]
private ParameterValue[] GetParametersValue(Hashtable properties)
{
ParameterValue[] retValues = new ParameterValue[properties.Count];
// Enumerate properties and create report server specific string.
IDictionaryEnumerator customPropEnumerator = properties.GetEnumerator();
int i = 0;
while ( customPropEnumerator.MoveNext() )
{
retValues[i] = new ParameterValue();
retValues[i].Name = customPropEnumerator.Key.ToString();
retValues[i].Value = customPropEnumerator.Value.ToString();
i++;
}
return retValues;
}
#endregion
В данном примере за основу взят web метод Render. Поэкспериментировав с оболочкой, нажимая правую клавишу мышки над отчетами в дереве, Вы сможете сохранить файлы экспорта в различных, доступных форматах.
Послесловие.
Очень надеюсь на то, что мне удалось и в этой статье передать Вам частичку своих знаний, которые помогут Вам создавать еще более удобные приложения со столь удачными продуктами Microsoft как Reporting Services. Возможно, я буду развивать данный продукт, наращивая в нем функциональность, но мне также очень хотелось бы услышать и Ваше мнение по данному продукту.
С уважением, Serg Vorontsov aka V©R©N.
Дайджест новых статей по интернет-маркетингу на ваш email
Новые статьи и публикации
- 2024-09-30 » Как быстро запустить Laravel на Windows
- 2024-09-25 » Next.js
- 2024-09-05 » OpenAI рассказал, как запретить ChatGPT использовать содержимое сайта для обучения
- 2024-08-28 » Чек-лист: как увеличить конверсию интернет-магазина на примере спортпита
- 2024-08-01 » WebSocket
- 2024-07-26 » Интеграция с Яндекс Еда
- 2024-07-26 » Интеграция с Эквайринг
- 2024-07-26 » Интеграция с СДЕК
- 2024-07-26 » Интеграция с Битрикс-24
- 2024-07-26 » Интеграция с Travelline
- 2024-07-26 » Интеграция с Iiko
- 2024-07-26 » Интеграция с Delivery Club
- 2024-07-26 » Интеграция с CRM
- 2024-07-26 » Интеграция с 1C-Бухгалтерия
- 2024-07-24 » Что такое сторителлинг: техники и примеры
- 2024-07-17 » Ошибка 404: что это такое и как ее использовать для бизнеса
- 2024-07-03 » Размещайте прайс-листы на FarPost.ru и продавайте товары быстро и выгодно
- 2024-07-01 » Профилирование кода в PHP
- 2024-06-28 » Изучаем ABC/XYZ-анализ: что это такое и какие решения с помощью него принимают
- 2024-06-17 » Зачем вам знать потребности клиента
- 2024-06-11 » Что нового в работе Яндекс Метрики: полный обзор обновления
- 2024-06-11 » Поведенческие факторы ранжирования в Яндексе
- 2024-06-11 » Скорость загрузки сайта: почему это важно и как влияет на ранжирование
- 2024-05-27 » Подборка сервисов для расшифровки аудио в текст
- 2024-05-27 » PostgreSQL 16. Изоляция транзакций. Часть 2
- 2024-05-06 » Как настраивать конверсионные стратегии: работа над ошибками
- 2024-04-22 » Комментирование кода и генерация документации в PHP
- 2024-04-22 » SEO в России и на Западе: в чем основные отличия
- 2024-04-22 » SEO для международного масштабирования
- 2024-04-22 » Как использовать XML-карты для продвижения сайта
Самый лучший человек тот, который живет преимущественно своими мыслями и чужими чувствами, самый худший сорт человека - который живет чужими мыслями и своими чувствами. Из различных сочетаний этих четырех основ, мотивов деятельности - все различие людей. Люди, живущие только своими чувствами, - это звери. Толстой Лев Николаевич - (1828-1910) - великий русский писатель. Его творчество оказало огромное влияние на мировую литературу |
Мы создаем сайты, которые работают! Профессионально обслуживаем и продвигаем их , а также по всей России и ближнему зарубежью с 2006 года!
Как мы работаем
Заявка
Позвоните или оставьте заявку на сайте.
Консультация
Обсуждаем что именно Вам нужно и помогаем определить как это лучше сделать!
Договор
Заключаем договор на оказание услуг, в котором прописаны условия и обязанности обеих сторон.
Выполнение работ
Непосредственно оказание требующихся услуг и работ по вашему заданию.
Поддержка
Сдача выполненых работ, последующие корректировки и поддержка при необходимости.