У меня есть несколько функций SQL CLR (UDF), которые считывают данные из внешней базы данных DB2, размещенной на IBM iSeries (используя IBM DB2 .Net Provider). Чтобы у функции были необходимые разрешения для чтения этих данных, мне нужно украсить функцию атрибутом SqlFunction, у которого для свойства DataAccess установлено значение DataAccessKind.Read. Я также развертываю сборку как UNSAFE.
Время, затрачиваемое на чтение данных из базы данных DB2, относительно велико (например, 3 мс для простейшего ExecuteScalar).
Я использую эти UDF для эффективного слияния данных из базы данных DB2 с представлениями Sql Server.
Например, предположим, что моя UDF определена как
[SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true)]
public static SqlMoney GetCostPrice(SqlString partNumber)
{
decimal costPrice;
// open DB2 connection and retrieve cost price for part
return new SqlMoney(costPrice);
}
а затем используйте это в моем представлении SQL как:
select Parts.PartNumber,
dbo.GetCostPrice(Parts.PartNumber) as CostPrice
from Parts
На проблемы с низкой производительностью можно было бы значительно повлиять, если бы я мог запускать свои представления SQL с планом параллельных запросов.
Существуют задокументированные методы принудительного выполнения плана запроса параллельно, а не последовательно, но эти методы имеют ограничения, налагаемые SQL Server, одно из которых заключается в том, что определенная в SQL CLR функция ДОЛЖНА иметь DataAccess = DataAccessKind.None.
Но если я установлю для DataAcessKind значение None, я получу исключение при попытке открыть любой DbConnection внутри функции.
И это моя проблема! Как я могу запустить свою определяемую пользователем функцию в плане параллельных запросов, при этом позволяя ей считывать данные из внешней базы данных?
Лучшая идея, которую я должен решить, - это жестко закодировать DataAccess = DataAccessKind.None в моем атрибуте SqlFunction, а затем во время выполнения в теле функции повысить разрешения с помощью Code Access Security, чтобы последующий код имел разрешения на открытие объектов DbConnection. .
Но я не могу понять, как это сделать? В качестве эксперимента я попробовал следующее
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlMoney TestFunction()
{
var sqlPerm = new SqlClientPermission(PermissionState.Unrestricted);
sqlPerm.Assert();
using (var conn = new SqlConnection("context connection=true"))
{
conn.Open();
}
return new SqlMoney();
}
и вызовите из Sql Server Management Studio с помощью:
select dbo.TestFunction()
но я продолжаю получать исключение безопасности...
Произошла ошибка .NET Framework во время выполнения определяемой пользователем подпрограммы или агрегата "TestFunction": System.InvalidOperationException: в этом контексте доступ к данным запрещен. Либо контекст является функцией или методом, не отмеченным DataAccessKind.Read или SystemDataAccessKind.Read, либо обратным вызовом для получения данных из метода FillRow табличной функции, либо методом проверки определяемого пользователем типа. System.InvalidOperationException: в System.Data.SqlServer.Internal.ClrLevelContext.CheckSqlAccessReturnCode(SqlAccessApiReturnCode eRc) в System.Data.SqlServer.Internal.ClrLevelContext.GetCurrentContext(приемник SmiEventSink, логический throwIfNotASqlClrThread, логический fAllowLinkImpersonationServer) в Microsoft.Server.SqlProperty .GetCurrentContext(SmiEventSink eventSink) в Microsoft.SqlServer.Server.SmiContextFactory.GetCurrentContext() в System.Data.SqlClient.SqlConnectionFactory.GetContextConnection(параметры SqlConnectionString, Object providerInfo, DbConnection owningConnection) в System.Data.SqlConnection.SqlConnectionFactory.CreateConnection(DbConnection параметры, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection) в System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) в System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection o wningConnection) в System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection externalConnection, DbConnectionFactory connectionFactory) в System.Data.SqlClient.SqlConnection.Open() в UserDefinedFunctions.UserDefinedFunctions.TestFunction()
У кого-нибудь есть идеи?
Заранее спасибо.
(кстати, я использую SQL 2008 с использованием .Net 3.5)