TSQL — выполнение разрешения CLR

Я получил процедуру sql из CLR (.net Assembly), которая при выполнении возвращает ошибку

Msg 6522, Level 16, State 1, Procedure sp_HelloWorld, Line 0
A .NET Framework error occurred during execution of user defined routine or aggregate 'sp_HelloWorld': 
System.Security.SecurityException: Request for the permission of type 'System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
System.Security.SecurityException: 
   at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
   at System.Security.PermissionSet.Demand()
   at System.Data.Common.DbConnectionOptions.DemandPermission()
   at System.Data.SqlClient.SqlConnection.PermissionDemand()
   at System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at HelloWorld.SQLCLR.HelloWorld()

Это мой SQL-скрипт

go
drop procedure HelloWorld
drop assembly HelloWorld
GO

create assembly HelloWorld from 'F:\HelloWorld.dll'
with permission_set = safe
Go
create procedure sp_HelloWorld
as external name HelloWorld.[HelloWorld.SQLCLR].HelloWorld
go
exec sp_HelloWorld

а это мой класс (сборка)

using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Security.Permissions;
using System.Data;

namespace HelloWorld
{
    public class SQLCLR
    {
        [Microsoft.SqlServer.Server.SqlProcedure]
        public static void HelloWorld()
        {
            string connectString1 = @"Data Source=localhost;Initial Catalog=ItemData;Integrated Security=True";

            SqlClientPermission permission = new SqlClientPermission(PermissionState.None);
            permission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);
            permission.PermitOnly();
            SqlConnection sqlcon = new SqlConnection(connectString1);
            sqlcon.Open();
            SqlCommand sqlcmd = new SqlCommand("SELECT Top 1 * FROM ItemData.dbo.Item", sqlcon);
            SqlDataReader reader = sqlcmd.ExecuteReader();
            SqlContext.Pipe.Send(reader);
            sqlcon.Close();
        }
    }
}

person Juvil    schedule 05.11.2010    source источник
comment
Ювил, если вы все еще заинтересованы, я добавил ответ, который объясняет проблему и исправляет код в соответствии с рекомендациями. Я также объяснил, почему другие ответы не работают (в комментариях к этим ответам).   -  person Solomon Rutzky    schedule 02.09.2015


Ответы (3)


Проблема просто в том, что вы пытаетесь получить доступ к внешнему ресурсу в сборке, помеченной как SAFE. Для доступа к внешним ресурсам необходимо установить для сборки не менее EXTERNAL_ACCESS (а в некоторых случаях UNSAFE). Однако, глядя на ваш код, вы просто пытаетесь подключиться к локальному экземпляру, и в этом случае есть гораздо более простой (и более быстрый) способ сделать это: использовать "Context Connection = true;" в качестве ConnectionString.

Контекстное соединение — это прямое соединение с текущим процессом/сеансом, которое иногда называют внутрипроцессным соединением. Преимущества использования Context Connection:

  • можно сделать в сборках, помеченных как SAFE
  • доступ к локальным временным объектам (временные таблицы и временные процедуры, имена которых начинаются с одинарного # вместо двойного ##)
  • доступ к SET CONTEXT_INFO и CONTEXT_INFO()
  • отсутствие накладных расходов на запуск соединения

Также:

  • независимо от того, используете ли вы внутрипроцессное, контекстное соединение или обычное/внешнее соединение, вам не нужно официально запрашивать разрешение с помощью SqlClientPermission
  • вы всегда должны очищать внешние ресурсы, вызывая их метод Dispose(). Не все объекты имеют это, но SqlConnection, SqlCommand и SqlDataReader точно есть. Обычно люди заключают одноразовые объекты в блок using(), поскольку это макрос компилятора, который расширяется до структуры try/finally, которая вызывает метод Dispose() в finally, чтобы гарантировать, что он будет вызван, даже если произойдет ошибка.
  • Метод Dispose() многих/большинства одноразовых объектов автоматически обрабатывает вызов Close(), поэтому обычно вам не нужно явно вызывать Close().

Ваш код должен выглядеть следующим образом:

[Microsoft.SqlServer.Server.SqlProcedure]
public static void HelloWorld()
{
  using (SqlConnection sqlcon = new SqlConnection("Context Connection = true;")
  {
    using (SqlCommand sqlcmd = new SqlCommand("SELECT Top 1 * FROM ItemData.dbo.Item",
               sqlcon))
    {
      sqlcon.Open();

      using (SqlDataReader reader = sqlcmd.ExecuteReader())
      {
        SqlContext.Pipe.Send(reader);
      }
    }
  }
}
person Solomon Rutzky    schedule 01.09.2015
comment
Отличная информация ... так много смысла иметь это. Но! Еще одна важная вещь, которую нам нужно... проверить, где мы работаем, чтобы выбрать между этой и типичной строкой, чтобы мы могли использовать типичную строку подключения во время разработки. Как мы можем проверить, есть ли у нас этот контекст сервера sql? - person Mike M; 03.02.2018
comment
Только что нашел именно ответ ... также написанный вами :). Возможно, вы могли бы добавить ссылку... stackoverflow.com/a/31279625/1518460 - person Mike M; 03.02.2018

Я просто хотел добавить к этому свои два смысла. Я делаю что-то очень похожее, и я получаю ту же ошибку. Вот что я нашел, но т.к. у меня нет такого уровня доступа к БД, я не могу его проверить.

Самый простой (хотя MSDN не рекомендует просто запускать процесс CLR) - установить уровень разрешений на External_Access...

Наборы разрешений на уровне политики узла SQL Server Набор разрешений безопасности доступа для кода, предоставляемых сборкам на уровне политики узла SQL Server, определяется набором разрешений, указанным при создании сборки. Существует три набора разрешений: SAFE, EXTERNAL_ACCESS и UNSAFE.

Уровень разрешений устанавливается на страницах свойств проекта CLR, вкладка базы данных — установите уровень разрешений — внешний, установите владельца сборки — dbo и запустите tsql «ALTER DATABASE DataBaseName SET TRUSTWORTHY ON». Это сделает работу ВЫПОЛНЕННОЙ! - и SmtpClient будет работать нормально... Тогда сделайте все правильно и подпишите сборку с помощью файла ключа со строгим именем...

Полное сообщение здесь...

person Ryan    schedule 19.06.2012
comment
Учитывая, что желание заключается в подключении к локальному экземпляру, это, безусловно, не самый простой и даже не лучший подход. И установка базы данных в TRUSTWORTHY ON, несмотря на то, что это быстро и легко, является плохим подходом, который обычно не нужен. Правда, в последнем утверждении говорится, что делайте это правильно..., но если кто-то заставит его работать с TRUSTWORTHY ON, то очень мало шансов, что он вернется, чтобы сделать это правильно позже. В любом случае, я объясняю реальную проблему в своем ответе. - person Solomon Rutzky; 02.09.2015

Вы установили для своей БД значение Trusrtworth ON и включили clr?

Попробуй это

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

ALTER DATABASE [YourDatabase] SET TRUSTWORTHY ON
GO

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

person Raymund    schedule 07.11.2010
comment
Это не помогло исправить это для меня. - person Ryan; 19.06.2012
comment
@ Райан и Раймунд: эти шаги не помогут. Если среда CLR еще не включена, попытка запустить любой код SQLCLR приведет к ошибке с сообщением о том, что интеграция со средой CLR не включена. Установка DB в TRUSTWORTHY ON, хотя это быстро и просто и обычно помогает для EXTERNAL_ACCESS или UNSAFE сборок, обычно не требуется, поскольку представляет угрозу безопасности. Проблема здесь в том, что для сборки установлено значение SAFE, которое запрещает любые внешние запросы. Шаг 1 — установить для сборки значение EXTERNAL_ACCESS, но есть еще лучший вариант, как показано в моем ответе. - person Solomon Rutzky; 02.09.2015
comment
Я согласен, что это не совсем по делу. Проблема в разрешениях. Другой комментарий и другой ответ попадают в суть дела. - person Mike M; 03.02.2018