SQL CLR: результаты потоковой табличной функции - PullRequest
3 голосов
/ 01 августа 2011

Моя проблема очень похожа на эту проблему.

Однако я использую SQL Server 2005 с пакетом обновления 2 (SP2) (v9.0.3042) и опубликованное там решение не работает для меня. Я пытался использовать обе строки подключения. Один закомментирован в моем коде.

Я понимаю, что могу сохранить все результаты в List или ArrayList в памяти и вернуть их. Я сделал это успешно, но это не цель здесь. Цель состоит в том, чтобы иметь возможность передавать результаты по мере их доступности.

Возможно ли это, используя мою версию SQL Server?

Вот мой код: (Обратите внимание, что параметры на самом деле не используются в настоящее время. Я сделал это для отладки)

public static class StoredProcs
{
    [SqlFunction(
        DataAccess = DataAccessKind.Read,
        SystemDataAccess=SystemDataAccessKind.Read,
        FillRowMethodName="FillBaseline",
        TableDefinition = "[baseline_id] [int], [baseline_name] [nvarchar](256), [description] [nvarchar](max), [locked] [bit]"
        )]
    public static IEnumerable fnGetBaselineByID(SqlString projectName, SqlInt32 baselineID)
    {
        string connStr = "context connection=true";
        //string connStr = "data source=.;initial catalog=DBName;integrated security=SSPI;enlist=false";
        using (SqlConnection conn = new SqlConnection(connStr))
        {
            conn.Open();
            using (SqlCommand cmd = new SqlCommand(String.Format(@"
                SELECT *
                FROM [DBName].[dbo].[Baseline] WITH (NOLOCK)
            "), conn))
            {
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        yield return new Baseline(reader);
                    }
                }
            }
        };
    }

    public static void FillBaseline(Object obj, out SqlInt32 id, out SqlString name, out SqlString description, out bool locked)
    {
        Baseline baseline = (Baseline)obj;
        id = baseline.mID;
        name = baseline.nName;
        description = baseline.mDescription;
        locked = baseline.mLocked;
    }
}

Вот часть моего сценария развертывания SQL:

CREATE ASSEMBLY [MyService_Stored_Procs]
FROM 'C:\temp\assemblyName.dll'
WITH PERMISSION_SET = SAFE

Когда я использую строку подключения "context connection = true", я получаю эту ошибку:

Произошла ошибка при получении новой строки из пользовательской таблицы Ценная функция: System.InvalidOperationException: доступ к данным не разрешен в этот контекст. Либо контекст - это функция или метод, не помеченный с DataAccessKind.Read или SystemDataAccessKind.Read, является обратным вызовом получить данные из метода FillRow табличной функции, или является Метод проверки UDT.

Когда я использую другую строку подключения, я получаю эту ошибку:

Произошла ошибка при получении новой строки из пользовательской таблицы Ценная функция: System.Security.SecurityException: Запрос на разрешение тип 'System.Data.SqlClient.SqlClientPermission, System.Data, Версия = 2.0.0.0, Культура = нейтральная, PublicKeyToken = b77a5c561934e089 ' не удалось.

Ответы [ 3 ]

5 голосов
/ 14 августа 2011

После дальнейших исследований, проб и ошибок я нашел свое решение.В статье , о которой я упоминал здесь , говорится:

ваша сборка должна быть создана с параметром allow_set = external_access

Это гораздо легче сказать, чем сделать, но это былохорошая отправная точка.Простое использование этой строки вместо access_set = safe приводит к ошибке:

CREATE ASSEMBLY для сборки 'assemblyName' не выполнен, поскольку сборка 'assemblyName' не авторизована для PERMISSION_SET = EXTERNAL_ACCESS.Сборка авторизуется, если выполняется одно из следующих условий: владелец базы данных (DBO) имеет разрешение EXTERNAL ACCESS ASSEMBLY, а база данных имеет свойство базы данных TRUSTWORTHY;или сборка подписана сертификатом или асимметричным ключом, который имеет соответствующий логин с разрешением EXTERNAL ACCESS ASSEMBLY.

Итак, первое, что мне нужно было сделать, это подписать мой файл dll.Для этого в Visual Studio 2010 перейдите на вкладку «Подписывание» в свойствах проекта, установите флажок «Подписать сборку» и дайте ему имя.Для этого примера имя MyDllKey.Я решил не защищать его паролем.Затем, конечно, я скопировал файл dll на сервер sql: C: \ Temp

. Используя эту страницу в качестве ссылки, я создал учетную запись SQL на основе указанного выше ключа, используя эти3 команды:

CREATE ASYMMETRIC KEY MyDllKey FROM EXECUTABLE FILE = 'C:\Temp\MyDll.dll'
CREATE LOGIN MyDllLogin FROM ASYMMETRIC KEY MyDllKey
GRANT EXTERNAL ACCESS ASSEMBLY TO MyDllLogin

После того, как логин создан, как описано выше, теперь я могу создать сборку, используя это:

CREATE ASSEMBLY [MyDll]
FROM 'C:\Temp\MyDll.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS

Теперь остается только использовать правильное соединениестрока.Видимо, использование enlist=false в сочетании с connection=true невозможно.Вот пример строки подключения, которую я использовал.

string connStr = @"data source=serverName\instanceName;initial catalog=DBName;integrated security=SSPI;enlist=false";

И это работает!

3 голосов
/ 17 января 2014

Первоначальная проблема связана с использованием ключевого слова yield в вашей функции, как объясняется в этом вопросе: SqlFunction не может открыть контекстное соединение, несмотря на наличие DataAccessKind.Read .

Если вы избегаете использования yield (сохраните результаты в промежуточном массиве, верните весь лот в конце), проблема исчезнет.

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

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

Вот ошибка в Connect: http://connect.microsoft.com/SQLServer/feedback/details/442200/sql-server-2008-clr-tvf-data-access-limitations-break-existing-code

1 голос
/ 31 декабря 2014

Google для этого:

Доступ к данным в этом контексте не разрешен.Либо контекст является функцией или методом, не помеченным DataAccessKind.Read или SystemDataAccessKind.Read, либо является обратным вызовом для получения данных из метода FillRow функции с табличной таблицей, либо является методом проверки UDT.

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

Звучит безобидно, но то, что я сделал, использовало те же типы данных (SqlChars, SqlBoolean, SqlInt32) для input-параметры метода, который я добавил.

private static ArrayList FlatFile(SqlChars Delimeter, SqlChars TextQualifier)

Видимо, использование этих типов данных для чего-либо кроме CLR SqlFunction или SqlProcedure иногда может дать вам этот тип загадочной ошибки.

Как только я удалил эти типы данныхв моем новом методе и с использованием C # (string, bool, int) ошибка, наконец, исчезла.

private static ArrayList FlatFile(string Delimeter, string TextQualifier)

ПРИМЕЧАНИЕ. Этот only выдавал ошибку при использовании Олицетворение , чтобы получить файл из другого домена.Когда я передавал файл через локальный домен, я не получил эту ошибку, и это меня оттолкнуло.

Надеюсь, это поможет вам в трудную минуту.Я отработал слишком много часов, устраняя неисправность.

...