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() 的更简单方法,您可以将具有SqlConnectionSqlCommand 提交到远程计算机。只要您不需要截取结果来对它们做任何事情,这将绕过定义结果集结构的需要。

【讨论】:

感谢您的彻底回复,我用新的错误消息更新了我的 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 函数返回错误尝试使用上下文创建表和插入行的主要内容,如果未能解决你的问题,请参考以下文章

CLR SQL Server UDF 问题

表值函数只返回 CLR 错误

2010.1.1 CLR 无法从 COM 上下文

SQL CLR 在 SQL Server 中随机失败

创建返回表的函数不起作用语法错误? SQL

SQL CLR 用户定义函数 (C#) 在返回的字符串中的每个现有字符之间添加空字符 (\0)