Функции SQLCLR - Скалярные пользовательские функции (UDF), Табличные функции (TVF), Пользовательские агрегаты (UDA) и методы в пользовательских типах (UDT) - при использовании подключения к контексту ( то есть ConnectionString = "Context Connection = true;"
), связаны большинством тех же ограничений, что и функции T-SQL, включая невозможность PRINT
или RAISERROR('message', 10, 1)
. Однако у вас есть несколько вариантов.
Прежде чем мы перейдем к этим вариантам, следует указать, что:
вам не нужно переключаться на использование хранимой процедуры. Если хотите функцию, то придерживайтесь функции.
добавление параметра «отладки» и изменение выходных данных для этого кажется немного экстремальным, поскольку функции UDF (T-SQL и SQLCLR) не допускают перегрузки. Следовательно, параметр отладки всегда будет в сигнатуре. Если вы хотите запустить отладку, просто создайте временную таблицу с именем #debug
(или что-то в этом роде) и протестируйте ее через SELECT OBJECT_ID(N'tempdb..#debug');
, используя "Context Connection = true;"
для ConnectionString (что быстро и может быть выполнено в безопасном режиме является частью одного сеанса, поэтому он может видеть временную таблицу). Получите результат этого от if (SqlCommand.ExecuteScalar() == DBNull.Value)
.
пожалуйста, не используйте глобальную (то есть статическую) переменную. это гораздо сложнее, чем необходимо, и требует (как правило), чтобы сборка была установлена на UNSAFE
, чего следует избегать, если это вообще возможно.
Итак, если вы можете хотя бы установить сборку на EXTERNAL_ACCESS
, тогда у вас есть несколько вариантов. И для этого не требуется установить для базы данных значение TRUSTWORTHY ON
. Это очень распространенное (и прискорбное) недоразумение. Вам просто нужно подписать сборку (что в любом случае является хорошей практикой), затем создать асимметричный ключ (в [master]
) из DLL, затем создать логин на основе этого асимметричного ключа и, наконец, предоставить логин EXTERNAL ACCESS ASSEMBLY
. После этого (один раз) вы можете выполнить любое из следующих действий:
записать сообщения в файл, используя File.AppendAllText (String path, String содержимое) . Конечно, если у вас нет доступа к файловой системе, это не так полезно. Если в сети есть общий диск, к которому можно получить доступ, то, пока учетная запись службы для службы SQL Server имеет разрешение на создание и запись файлов в этот общий ресурс, это будет работать. Если есть общий ресурс, к которому у учетной записи службы нет прав, но у вашей учетной записи домена / Active Directory, вы можете заключить этот File.AppendAllText
вызов в:
using (WindowsImpersonationContext _Impersonate =
SqlContext.WindowsIdentity.Impersonate())
{
File.AppendAllText("path.txt", _DebugMessage);
_Impersonate.Undo();
}
подключиться к SQL Server и записать сообщения в таблицу. Это может быть текущий / локальный SQL Server или любой другой SQL Server. Вы можете создать таблицу в [tempdb]
, чтобы она автоматически очищалась при следующем перезапуске SQL Server, но в противном случае будет действовать до этого времени или до тех пор, пока вы его не удалите. Создание регулярного / внешнего соединения позволяет вам делать DML-операторы. Затем вы можете выбрать из таблицы, как вы запускаете функцию.
записать сообщения в переменную окружения. Начиная с Vista / Server 2008 переменные окружения точно не ограничены в размерах, хотя на самом деле они не обрабатывают переводы строк. Но любая переменная, установленная в коде .NET, также будет существовать до перезапуска службы SQL Server. И вы можете добавить сообщение, прочитав текущее значение и конкатенируя новое сообщение до конца. Что-то вроде:
{
string _Current = System.Environment.GetEnvironmentVariable(_VariableName,
EnvironmentVariableTarget.Process);
System.Environment.SetEnvironmentVariable(
_VariableName,
_Current + _DebugMessage,
EnvironmentVariableTarget.Process);
}
Следует отметить, что в каждом из этих трех случаев предполагается, что тестирование выполняется однопоточным способом. Если функция будет запущена из нескольких сеансов одновременно, то вам нужен способ разделения сообщений. В этом случае вы можете получить текущий идентификатор транзакции (все запросы, даже если BEGIN TRAN
не является транзакцией!), Который должен быть согласованным для любого конкретного выполнения (при многократном использовании в одной и той же функции, а также если функция вызывается для каждой строки через несколько строк). Это значение можно использовать в качестве префикса для сообщений, если используются методы переменных файла или среды, или в качестве отдельного поля при сохранении в таблице. Вы можете получить транзакцию, выполнив следующие действия:
int _TransactionID;
using (SqlConnection _Connection = new SqlConnection("Context Connection = true;"))
{
using (SqlCommand _Command = _Connection.CreateCommand())
{
_Command.CommandText = @"
SELECT transaction_id
FROM sys.dm_exec_requests
WHERE session_id = @@SPID;
";
_Connection.Open();
_TransactionID = (int)_Command.ExecuteScalar();
}
}
Дополнительная информация о функциях T-SQL и SQLCLR
Следующий список первоначально был взят со страницы MSDN для Создание пользовательских функций (компонент Database Engine) , а затем отредактирован мной, как уже отмечалось, чтобы отразить различия между функциями T-SQL и функциями SQLCLR. :
- Пользовательские функции не могут использоваться для выполнения действий, которые изменяют состояние базы данных.
- Пользовательские функции не могут содержать предложение OUTPUT INTO, целью которого является таблица.
- Пользовательские функции не могут возвращать несколько результирующих наборов. Используйте хранимую процедуру, если вам нужно вернуть несколько наборов результатов.
- Обработка ошибок ограничена в пользовательской функции. UDF не поддерживает TRY… CATCH, @@ ERROR или RAISERROR. [ Примечание: это с точки зрения T-SQL, как собственного, так и переданного из функции SQLCLR. Вы можете использовать try / catch / finally / throw в коде .NET. ]
- Операторы SET недопустимы в определяемой пользователем функции.
- Предложение FOR XML не допускается
- Пользовательские функции могут быть вложенными; ... Уровень вложенности увеличивается, когда вызываемая функция начинает выполнение, и уменьшается, когда вызываемая функция заканчивает выполнение. Пользовательские функции могут быть вложены до 32 уровней.
- Следующие операторы компонента Service Broker не могут быть включены в определение пользовательской функции Transact-SQL:
- НАЧАЛО ДИАЛОГОВОГО РАЗГОВОРА
- КОНЕЦ КОНВЕРСАЦИИ
- GET CONVERSATION GROUP
- ПЕРЕМЕЩЕНИЕ ПЕРЕМЕЩЕНИЯ
- ПРИЕМ
- Отправить
Следующее относится и к функциям T-SQL, и к функциям SQLCLR:
- Невозможно использовать PRINT
- Невозможно вызвать NEWID () [Ну, если вы не
SELECT NEWID()
из вида. Но внутри кода .NET вы можете использовать Guid.NewGuid()
. ]
Следующее относится только к функциям T-SQL:
- Пользовательские функции не могут вызывать хранимую процедуру, но могут вызывать расширенную хранимую процедуру.
- Пользовательские функции не могут использовать динамический SQL или временные таблицы. Табличные переменные допускаются.
Напротив, функции SQLCLR могут:
- Выполнять хранимые процедуры, если они доступны только для чтения.
- Использовать динамический SQL (все SQL, представленные из SQLCLR, являются специальными / динамическими по самой своей природе).
- ВЫБРАТЬ из временных таблиц.