SQL CLR 函数返回错误尝试使用上下文创建表和插入行
Posted
技术标签:
【中文标题】SQL CLR 函数返回错误尝试使用上下文创建表和插入行【英文标题】:SQL CLR function returning error trying to use context to create table and insert rows 【发布时间】:2015-01-29 18:36:06 【问题描述】:我在带有 .NET 4.5 的 SQL Server 2012 和 Visual Studio 2012 上运行,尝试构建一个 CLR 函数,该函数创建一个表并使用上下文连接将结果插入其中。
我收到了错误:
RunFetchXML 中发生错误:在此上下文中不允许数据访问。上下文要么是未使用 DataAccessKind.Read 或 SystemDataAccessKind.Read 标记的函数或方法,要么是从表值函数的 FillRow 方法获取数据的回调,要么是 UDT 验证方法。
更新: 我能够克服上面的错误,我发现在我添加 DataAccessKind.Read 属性后程序集没有按预期更新,所以在更新后我再次尝试并收到此错误:
An error occurred in RunFetchXML: Invalid use of a side-effecting operator 'CREATE TABLE' within a function.
我正在尝试设置一个将FetchXML
作为输入字符串的 CLR 函数,CLR 函数将需要使用该FetchXML
并查询外部系统,然后创建一个表并转储结果进去。我不认为我可以将表值函数与FillRow
等一起使用,因为我需要能够处理FetchXML
中定义的任何内容,这意味着最终结果可以是任何东西,它不是固定数据结构。
所以我想做的是从 CLR 内部使用当前上下文连接回 SQL Server,创建一个支持 FetchXML
返回的字段的表,然后将查询结果转储到表中。
我在尝试这个时遇到了上述错误,我不确定这是否可能,任何帮助或建议将不胜感激。
这是我当前的 CLR 函数代码,这被简化为只是尝试创建一个空表,并返回一个带有“成功”或错误消息的字符串结果,这就是我得到上述错误的地方来自:
Imports System.Data
Imports System.Data.SqlTypes
Imports System.Data.Sql
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlClient
Public Class Retrieve
<SqlFunction(DataAccess:=DataAccessKind.Read)> _
Public Shared Function RunFetchXML(pstrFetchXML As SqlString) As SqlString
Dim lstrResult As SqlString
Try
Using lsqlConn As New SqlConnection("context connection=true")
lsqlConn.Open()
Using lsqlCmd As New SqlCommand("CREATE TABLE #test (vchField1 varchar(200), iField2 int)", lsqlConn)
lsqlCmd.CommandType = CommandType.Text
lsqlCmd.ExecuteNonQuery()
End Using
End Using
lstrResult = "Success"
Catch ex As Exception
lstrResult = "An error occurred in RunFetchXML: " + ex.Message
End Try
Return lstrResult
End Function
End Class
这是我用来注册的 SQL 命令的相关部分:
CREATE ASSEMBLY [TestAssembly]
FROM 'C:\Mssql\CLR\TestAssembly.dll'
WITH PERMISSION_SET = UNSAFE
GO
CREATE FUNCTION RunFetchXML(@pstrFetchXML nvarchar(MAX)) RETURNS nvarchar(MAX)
AS
EXTERNAL NAME TestAssembly.[TestAssembly.Retrieve].RunFetchXML
GO
Grant Execute on RunFetchXML TO Public;
我运行这个命令来测试它并得到错误信息作为返回值:
DECLARE @vchOutput nvarchar(MAX)
EXEC @vchOutput = dbo.RunFetchXML['test']
SELECT @vchOutput
我只是在这里遗漏了一些东西,还是无法从 CLR 函数写入(创建表并插入)?
【问题讨论】:
【参考方案1】:函数,无论是 T-SQL 还是 SQLCLR,都不能修改服务器的状态。有一整套无法完成的事情,例如任何SET
命令、调用NEWID()
等。请查看Create User-defined Functions (Database Engine) 的MSDN 页面以获取“限制和限制”列表。 SQLCLR 函数的唯一区别是它们可以执行 T-SQL 存储过程,但前提是它们又不违反任何这些限制。
因此,您可以继续使用函数,但将SqlConnection
更改为使用真实/外部连接,因为这将被视为任何其他客户端连接并且没有此类限制。当然,这会阻止您创建本地临时表的能力,因为该临时表将位于不同的 Session 上,并且会在函数结束后消失。
相反,将函数切换为存储过程:
Procs 没有这样的限制。唯一真正的缺点是它们不太容易在查询中使用或与结果交互。 这将允许您创建一个本地临时表,尽管它可能在 proc 完成后不存在,因为它是在子进程中创建的,就像创建临时对象的动态 SQL 一样,它们不存在一次EXEC
结束。
您实际上不需要创建临时表,因为存储过程可以返回未硬编码到应用代码中的动态结果集。
补充说明:
dbo.RunFetchXML['test']
是一种奇怪的语法,即使用于执行像存储过程这样的函数(这很好,但参数的语法仍然很奇怪)。应该是SET @vchOutput = dbo.RunFetchXML('test');
这与 XML 有什么关系?如果您只是将结果集返回给客户端,还有一种使用SqlContext.Pipe.ExecuteAndSend()
的更简单方法,您可以将具有SqlConnection
的SqlCommand
提交到远程计算机。只要您不需要截取结果来对它们做任何事情,这将绕过定义结果集结构的需要。
【讨论】:
感谢您的彻底回复,我用新的错误消息更新了我的 OP,它支持您所说的有关限制的内容。为了填写更多详细信息,我使用 FetchXML 的原因是为了在线对 Dynamics CRM 执行该查询,此 CLR 函数必须引用 CRM SDK 程序集才能执行查询并取回结果。这就是 xml 和结果集的动态结构的原因,很抱歉对 OP 中的细节有所了解。您对临时表提出了一个很好的观点,我希望保留它,以便表名可以作为输入参数。 我对 CLR 存储过程选项不是很熟悉,我得研究一下。另外,我有兴趣尝试 ExecuteAndSend,但我不确定它是如何工作的,因为我很可能必须处理结果集。 FetchXML 的结果很可能是某种我必须处理和插入的通用 List(Of Something)。我想知道的另一个选项是如果允许 INSERT 语句,我可以在进入 CLR 函数之前预先创建表,或者按照您的建议,使用单独的连接和“真实”表。跨度> Re: 函数调用的语法,我试过dbo.RunFetchXML('test')
但这给了我一个错误Incorrect syntax near 'test'.
不确定是什么问题,但括号有效。另外,UNSAFE 的原因是因为我最终需要引用 CRM SDK dll,除非当第三方 dll 是依赖项时,EXTERNAL_ACCESS 仍然可以工作?
@AaronKoehler 我用有关功能限制的信息更新了第一段。我意识到[
和]
的奇怪语法存在问题,并更新了注释中的要点。关于希望与 Dynamics CRM 进行沟通:1)这是帮助解决此问题所需的关键信息,应该在问题中,2)您是否搜索过?听起来很熟悉。也许其他人已经做到了?如果不是 SQL 查询,ExecuteAndSend
将不起作用。在 proc 或使用常规/外部连接的函数中将允许 INSERT。可能不需要 UNSAFE;不先尝试。以上是关于SQL CLR 函数返回错误尝试使用上下文创建表和插入行的主要内容,如果未能解决你的问题,请参考以下文章