46378 (665526), страница 2

Файл №665526 46378 (MS SQL Server 9 “Yukon”. Интеграция с .NET) 2 страница46378 (665526) страница 22016-07-31СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 2)

Здравствуй, мир

Останавливаться на том, каким образом хранимая процедура обрабатывает данные, смысла нет – это обычный C#, и его особенности хорошо известны. Давайте научим ее общаться с внешним миром. Для начала доведем ее до уровня Кернигана и Ритчи:

using System;

using System.Data;

using System.Data.Sql;

using System.Data.SqlServer;

using System.Data.SqlTypes;

public class StoredProcedure

{

[SqlProcedure("HelloWorld")]

public static void MyProcedure()

{

SqlContext.GetPipe().Send("Hello, Yukon!");

}

};

Эта процедура демонстрирует еще один важный компонент, связывающий .NET с MS SQL Server: класс System.Data.SqlServer.SqlContext. Этот класс содержит несколько статических методов, обеспечивающих доступ к контексту, в котором выполняется код. В данном случае мы получаем доступ к объекту класса System.Data.SqlServer.SqlPipe, который представляет серверную сторону соединения с клиентом. Именно в эту «трубу» SQL Server отправляет результаты выполнения запросов. Если хранимая процедура должна возвращать какие-то данные в клиентское приложение, то без SqlPipe не обойтись.

В этом примере мы используем метод SqlPipe.Send(String msg), предназначенный для отправки текстовых сообщений. Его функциональность аналогична команде print в T-SQL. Остальные методы SqlPipe предназначены для отправки табличных данных:

Метод или свойство

Описание

public void Execute (System.Data.SqlServer.SqlCommand command )public void Execute

(System.Data.SqlServer.SqlExecutionContext request )

Выполняет указанную команду или запрос и возвращает результат клиенту. Аналог выполнения оператора SELECT … FROM … в хранимой процедуре на T-SQL.

public void Send (System.Data.SqlServer.SqlError se)

Возвращает клиенту указанную ошибку.

public void Send (System.Data.Sql.ISqlReader reader)

Отправляет клиенту все записи из указанного набора.

public void SendResultsStart (System.Data.Sql.ISqlRecord record , bool sendRow)

Посылает клиенту первую запись в наборе записей. Устанавливает свойство SendingResults в true.

public System.Boolean SendingResults { get; }

Указывает, что процесс отправки набора записей не окончен.

public void SendResultsRow (System.Data.Sql.ISqlRecord record)public void Send (System.Data.Sql.ISqlRecord record )

Посылает клиенту очередную запись в наборе. Требует SendingResults == true.

public void SendResultsEnd ( )

Сигнализирует об окончании набора записей и устанавливает свойство SendingResults в false.

Таблица 2.

Таким образом, помимо передачи клиенту набора данных, полученного от сервера, можно формировать результаты вручную. С точки зрения клиента это будет выглядеть как обычный набор записей.

Возвращаем произвольные данные

Пока что документация весьма скупо освещает этот вопрос, но после нескольких экспериментов мне удалось создать вот такую процедуру:

[SqlProcedure()]

public static void CurrencyCourse(

[SqlMapping(typeof(SqlDateTime))] DateTime start,

[SqlMapping(typeof(SqlDateTime))] DateTime end)

{

using (SqlCommand cmd = SqlContext.GetCommand())

{

cmd.CommandText = @"

select changeDate, course from Course

where changeDate between @start and @end";

cmd.Parameters.AddWithValue("@start", start);

cmd.Parameters.AddWithValue("@end", end);

DateTime current = start;

SqlDecimal course = SqlDecimal.Null; // сначала курс отсутствует;

SqlMetaData[] recstruct = new SqlMetaData[2];

recstruct[0] = new SqlMetaData("D", SqlDbType.DateTime);

recstruct[1] = new SqlMetaData("course", SqlDbType.Decimal, 10, 4);

SqlDataRecord rec = new SqlDataRecord(recstruct);

SqlPipe pipe = SqlContext.GetPipe();

pipe.SendResultsStart(rec, false);

using (SqlDataReader r = cmd.ExecuteReader())

{

while (r.Read())

{

rec.SetSqlDecimal(1, course);

while(current < r.GetDateTime(0))

{

rec.SetDateTime(0, current);

pipe.SendResultsRow(rec);

current = current.AddDays(1);

}

course = r.GetDecimal(1);

}

}

rec.SetSqlDecimal(1, course);

while (current <= end)

{

rec.SetDateTime(0, current);

pipe.SendResultsRow(rec);

current = current.AddDays(1);

}

pipe.SendResultsEnd();

}

}

Эта процедура превращает данные в таблице изменения курсов некой валюты (Course) в таблицу ежедневных значений курса, повторяя предыдущее значение для тех дней, в которые изменений не происходило.

На этот раз у процедуры есть параметры. Чтобы помочь инструментам автоматического развертывания (например, той же MS VS Whidbey) определить SQL-типы параметров хранимой процедуры, для параметров метода можно указать атрибут SqlMapping (System.Data.Sql.SqlMappingAttribute). Его единственный параметр и задает тип для параметра процедуры. В данном случае этот атрибут является избыточным – параметры типа DateTime автоматически отображаются в тип SQL datetime (которому соответствует тип CLR System.Data.SqlTypes.SqlDateTime), но в более сложных случаях им придется пользоваться для устранения неоднозначности.

Чтобы выполнить запрос к данным сервера, мы воспользуемся еще одним статическим методом класса SqlContext – SqlContext.GetCommand().

Чтобы возвратить данные клиенту, нужен экземпляр класса, реализующего интерфейс System.Data.Sql.ISqlRecord. В данном случае использован System.Data.Sql.SqlDataRecord. Его конструктор требует указать желаемую структуру записи. Эта структура описывается массивом объектов класса System.Data.Sql.SqlMetaData. В каждом объекте задается имя и тип соответствующей колонки. Мы описываем структуру, соответствующую в терминах SQL вот такой «таблице»:

(

D datetime,

course decimal(10, 4)

)

Создав запись, мы инициируем процесс отправки при помощи вызова:

pipe.SendResultsStart(rec, false);

Второй параметр говорит о том, что саму запись отправлять клиенту не нужно; вместо этого метаданные записи используются для инициализации отправляемого набора записей.

Дальше все просто – мы читаем очередную запись из SqlDataReader, полученного в результате исполнения команды, заполняем поля в SqlDataRecord, и отправляем ее клиенту. Дополнительный цикл в конце досылает записи для дат между последним изменением и концом запрошенного интервала.

Отправив все, что хотелось, мы сигнализируем клиенту об окончании набора при помощи вызова

pipe.SendResultsEnd();

Стоит отметить, что результаты возвращаются напрямую клиенту, т.е. код, который вызвал процедуру, не имеет над этим процессом никакого контроля. Повторное использование такого кода в серверной части приложения маловероятно. В следующем разделе мы узнаем о том, как можно обойти это ограничение.

Функции

В рамках T-SQL функции делятся на два вида: скалярные и табличные.

ПРИМЕЧАНИЕ

Есть еще агрегатные функции, но их реализация существенным образом отличается от «обычных», и поэтому мы рассмотрим их в следующем разделе.

С точки зрения .NET, эти два типа функций устроены почти одинаково. Как и хранимые процедуры, они реализуются при помощи статических методов класса. Отличие заключается в том, как они возвращают значения. Есть три варианта:

Возвращаем значение произвольного типа. Это скалярная функция.

Возвращаем System.Data.Sql.ISqlReader. Структура данных в нем должна совпадать с декларированной структурой результата функции. Это табличная функция.

Возвращаем void. Внутри функции вручную формируем возвращаемые данные через SqlContext.GetReturnResultSet(). Это тоже табличная функция.

Все эти варианты подробно рассмотрены далее.

ПРИМЕЧАНИЕ

В отличие от встроенных функций, обращаться к «самодельным» нужно с уважением – предваряя имя функции именем схемы (которое по умолчанию совпадает с именем ее владельца). Например, я вызывал функцию из следующего подраздела примерно вот так:

select dbo.RevertString(“Beavis rulez”)

Скалярные функции

Это самая простая разновидность функций. В качестве примера напишем свой вариант встроенной функции reverse:

[SqlFunc()]

[SqlFunction(

DataAccess = DataAccessKind.None,

SystemDataAccess = SystemDataAccessKind.None,

IsDeterministic = true,

IsPrecise = true)]

public static SqlString RevertString(SqlString str)

{

if (str.IsNull)

return SqlString.Null;

System.Text.StringBuilder sb = new

System.Text.StringBuilder(str.Value.Length);

for (int i=str.Value.Length-1; i>=0; i--)

sb.Append(str.Value[i]);

return new SqlString(sb.ToString());

}

Поскольку реализация самой функции примитивна, остановимся на том, что ее окружает.

Во-первых, к методу применен атрибут SqlFunc. Как и SqlProcedure, он позволяет указать средствам автоматического развертывания информацию, необходимую для правильного построения команды CREATE FUNCTION. В данном случае никаких параметров не использовано – атрибут просто указывает, что данный метод надо будет зарегистрировать как функцию. Более подробно мы рассмотрим возможности этого атрибута чуть позже.

А вот следующий атрибут – SQLFunction – уже используется «внутри» MS SQL Server для определения того, как можно эту функцию использовать. В таблице 3 приведено описание параметров этого атрибута:

Имя параметра

Описание

DataAccess

Какой доступ осуществляет функция к пользовательским данным в базе:DataAccessKind.None – никакого.DataAccessKind.Read – читает данные.

SystemDataAccess

Какой доступ осуществляет функция к системным данным в базе:SystemDataAccessKind.None – никакого.SystemDataAccessKind.Read – читает данные.

IsDeterministic

Является ли функция детерминистической, т.е. зависит ли ее возвращаемое значение только от переданных параметров.

IsPrecise

Выполняет ли функция округления в процессе работы.

Таблица 3.

В нашем случае ни к каким данным доступа не происходит, возвращаемое значение зависит только от переданного параметра, и значение является точным, а не приближенным.

ПРИМЕЧАНИЕ

Это позволяет использовать эту функцию в максимально широком контексте – например, можно создать вычисляемую колонку на ее основе, и даже индекс по этой колонке. Это может быть полезно для сортировки, например, списка получателей e-mail. Сортировка по обращенному адресу поставит рядом адреса в одном домене, и можно будет оптимизировать рассылку писем.

Возвращаем ISqlReader

Во многих случаях табличная функция выполняет роль параметризованного view – данные берутся из таблиц, и, после применения операторов SQL к исходным данным и параметрам, результат возвращается в вызывающий код. Создадим функцию, которая будет возвращать список изменений курса валют, произшедших в заданном диапазоне дат:

[SqlFunc(TableDefinition = "D datetime, course decimal(10, 4)")]

[SqlFunction(DataAccess = DataAccessKind.Read,

SystemDataAccess = SystemDataAccessKind.None,

IsDeterministic = false, IsPrecise = true)]

public static ISqlReader GetCourseChanges(DateTime start, DateTime end)

{

SqlCommand cmd = SqlContext.GetCommand();

cmd.CommandText = @"

select changeDate, course from Course

where changeDate between @start and @end";

cmd.Parameters.AddWithValue("@start", start);

cmd.Parameters.AddWithValue("@end", end);

return cmd.ExecuteReader();

}

ПРЕДУПРЕЖДЕНИЕ

Увы, пока что мне не удалось заставить этот пример работать. Сервер неуклонно возвращает ошибку «Reader is closed». Каким образом избежать закрытия Reader после возвращения его серверу, я пока не понял.

Работаем с SqlResultSet

Для тех случаев, когда необходимо сформировать возвращаемый набор данных вручную, предусмотрен доступ к нему через метод контекста SqlContext.GetReturnResultSet(). Объект, возвращаемый этим методом, уже проинициализирован в соответствии с декларированной структурой функции. В него нужно добавить требуемые записи. В принципе, можно как добавлять, так и удалять/изменять записи, если это кажется необходимым. Воспроизведем поведение хранимой процедуры CurrencyCourse, созданной в конце предыдущего раздела:

[SqlFunc(TableDefinition = "D datetime, course decimal(10, 4) NULL")]

[SqlFunction(DataAccess = DataAccessKind.Read,

SystemDataAccess = SystemDataAccessKind.None,

IsDeterministic = false, IsPrecise = true)]

public static void GetCourseTable(DateTime start, DateTime end)

{

using (SqlCommand cmd = SqlContext.GetCommand())

{

cmd.CommandText = @"

select changeDate, course from Course

where changeDate between @start and @end";

cmd.Parameters.AddWithValue("@start", start);

cmd.Parameters.AddWithValue("@end", end);

DateTime current = start;

SqlDecimal course = SqlDecimal.Null;

SqlResultSet source = cmd.ExecuteResultSet(ResultSetOptions.None);

SqlResultSet dest = SqlContext.GetReturnResultSet();

SqlDataRecord rec;

while (source.Read())

{

while (current < source.GetDateTime(0))

{

rec = dest.CreateRecord();

rec.SetSqlDecimal(1, course);

rec.SetDateTime(0, current);

dest.Insert(rec);

current = current.AddDays(1);

}

course = source.GetDecimal(1);

}

while (current <= end)

{

rec = dest.CreateRecord();

rec.SetDateTime(0, current);

rec.SetSqlDecimal(1, course);

dest.Insert(rec);

current = current.AddDays(1);

}

}

}

Обратите внимание, что теперь в атрибуте SqlFunction содержится значение свойства DataAccess = DataAccessKind.Read, указывая на то, что функция читает данные из базы.

ПРЕДУПРЕЖДЕНИЕ

Обратите внимание также на то, что на этот раз для доступа к данным мы используем SqlResultSet вместо SqlDataReader. Дело в том, что одновременно читать из базы и работать с возвращаемым набором записей нельзя – возникает исключение с сообщением о том, что данное соединение уже используется. Возможно, данная особенность поведения будет изменена при выпуске финальной версии. Но пока единственным способом написать подобную функцию является чтение данных целиком до начала формирования выходного набора данных.

Агрегирующие функции

Характеристики

Тип файла
Документ
Размер
263,85 Kb
Тип материала
Учебное заведение
Неизвестно

Список файлов реферата

Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6618
Авторов
на СтудИзбе
295
Средний доход
с одного платного файла
Обучение Подробнее