Kev*_*Kev 5 .net sql-server sqlclr code-access-security elevated-privileges
我编写了许多 SQL CLR 函数 (UDF),它们从托管在 IBM iSeries 上的外部 DB2 数据库(使用 IBM DB2 .Net Provider)读取数据。为了使函数具有读取此数据的必要权限,我需要使用 SqlFunction 属性装饰该函数,并将 DataAccess 属性设置为 DataAccessKind.Read。我还将程序集部署为 UNSAFE。
从 DB2 数据库读取数据所花费的时间相对较慢(例如,最简单的 ExecuteScalar 需要 3 毫秒)。
我使用这些 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);
}
Run Code Online (Sandbox Code Playgroud)
然后在我的 SQL 视图中使用它作为:
select Parts.PartNumber,
dbo.GetCostPrice(Parts.PartNumber) as CostPrice
from Parts
Run Code Online (Sandbox Code Playgroud)
如果我可以使用并行查询计划运行我的 SQL 视图,那么糟糕的性能问题可能会受到显着影响。
有关于如何强制查询计划并行而不是串行运行的文档技术,但这些技术受到 SQL Server 的限制,其中之一是 SQL CLR 定义的函数必须具有 DataAccess = DataAccessKind.None。
但是,如果我将 DataAcessKind 设置为 None ,那么在尝试打开函数内的任何 DbConnection 时会出现异常。
这就是我的问题!如何在并行查询计划中运行我的 UDF,同时仍然允许它从外部数据库读取数据?
我必须解决的最佳想法是在我的 SqlFunction 属性中对 DataAccess = DataAccessKind.None 进行硬编码,然后在运行时在函数体内使用代码访问安全性提升权限,以便后续代码有权打开 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();
}
Run Code Online (Sandbox Code Playgroud)
并从 Sql Server Management Studio 调用:
select dbo.TestFunction()
Run Code Online (Sandbox Code Playgroud)
但我继续收到安全异常...
在执行用户定义的例程或聚合“TestFunction”期间发生 .NET Framework 错误:System.InvalidOperationException:在此上下文中不允许数据访问。上下文要么是未用 DataAccessKind.Read 或 SystemDataAccessKind.Read 标记的函数或方法,要么是从表值函数的 FillRow 方法获取数据的回调,要么是 UDT 验证方法。System.InvalidOperationException:在 System.Data.SqlServer.Internal.ClrLevelContext.CheckSqlAccessReturnCode(SqlAccessApiReturnCode eRc) 在 System.Data.SqlServer.Internal.ClrLevelContext.GetCurrentContext(SmiEventSink sink, Boolean throwIfNotASqlClrThread, Boolean fAllowImpersonation) 在 Microsoft.ProSqlLinkServer。 .GetCurrentContext(SmiEventSink eventSink) 在 Microsoft.SqlServer.Server.SmiContextFactory。
有人有任何想法吗?
提前致谢。
(顺便说一句,我使用 .Net 3.5 在 SQL 2008 上运行)
据我的测试(针对SqlConnection
SQL Server)显示,这只能通过使用常规/外部连接(即不是 Context Connection = true
)并将关键字添加Enlist
到连接字符串(设置为false
:
Server=DB2; Enlist=false;
Run Code Online (Sandbox Code Playgroud)
但在使用时似乎没有任何方法可以使其工作Context Connection = true
。上下文连接自动成为当前事务的一部分,并且在使用上下文连接时不能指定任何其他连接字符串关键字。交易与它有什么关系?好吧,默认值Enlist
是true
,所以即使您确实有常规/外部连接,如果您不指定Enlist=false;
,那么您也会得到相同的结果
在此上下文中不允许数据访问。
您现在遇到的错误。
当然,这是一个没有实际意义的问题,因为在这种特殊情况下使用上下文连接是没有意义的,因为它需要使用链接服务器,并且在对该问题的评论中指出“链接服务器 Db2提供商(来自 MS)速度慢得令人难以置信”。
还有人指出,也许TransactionScope
与 选项一起使用Suppress
可能会起作用。这是行不通的,因为如果 和都设置为(这是它们的默认值),则不允许您实例化TransactionScope
对象(使用三个选项中的任何一个:Required
、RequiresNew
或)。Suppress
DataAccess
SystemDataAccess
None
另外,关于愿望
在运行时提升 UDF 的 UserDataAccess 状态。
由于UserDataAccess
不是运行时选项,这是不可能的。它是在CREATE FUNCTION
执行语句时确定的(具有AS EXTERNAL NAME [Assembly]...
定义的语句)。UserDataAccess
和属性是与函数一起存储的元数据。您可以使用内置函数SystemDataAccess
查看其中任何一个的设置:OBJECTPROPERTYEX
SELECT OBJECTPROPERTYEX(OBJECT_ID(N'SchemaName.FunctionName'), 'UserDataAccess');
Run Code Online (Sandbox Code Playgroud)
您的两个选择似乎是:
使用支持该关键字的提供程序Enlist
,以便可以将其设置为 false,或者如果默认情况下未登记,则不需要将其DataAccess
设置为Read
。根据建议查看的文档(Integrating DB2 Universal Universal Database for iSeries with for iSeries with Microsoft ADO .NET),选项如下:
构建一个中间撕裂作为 SQLCLR 函数可以将请求传递到的 Web 服务,它将使用任何提供程序来获取信息,并且它将用该信息进行响应。然后,SQLCLR 函数不会进行任何直接数据访问,并且Web 服务可以进行自己的缓存(您说源数据不会经常更改)以提高性能(即使只缓存值 1 - 5 分钟) )。是的,这确实引入了外部依赖项,但它应该按预期工作。