SQL CLR 函数在尝试使用上下文创建 table 和插入行时返回错误

SQL CLR function returning error trying to use context to create table and insert rows

我 运行正在使用 .NET 4.5 SQL Server 2012 和 Visual Studio 2012,尝试构建一个创建 table 并插入结果的 CLR 函数使用上下文连接进入其中。

我遇到错误:

An error occurred in RunFetchXML: Data access is not allowed in this context. Either the context is a function or method not marked with DataAccessKind.Read or SystemDataAccessKind.Read, is a callback to obtain data from FillRow method of a Table Valued Function, or is a UDT validation method.

更新: 我能够克服上面的错误,我发现在我添加 DataAccessKind.Read 属性后程序集没有按预期更新,所以在更新之后我再次尝试并得到这个错误:

An error occurred in RunFetchXML: Invalid use of a side-effecting operator 'CREATE TABLE' within a function.

我正在尝试设置一个将 FetchXML 作为输入字符串的 CLR 函数,CLR 函数将需要采用该 FetchXML 并查询外部系统,然后创建a table 并将结果转储到其中。我认为我不能将 table 值函数与 FillRow 等一起使用,因为我需要能够处理 FetchXML 中定义的任何内容,这意味着结束结果可以是任何东西,它不是固定的数据结构。

所以我想做的是从 CLR 中使用当前上下文连接回 SQL 服务器,创建一个支持字段 return 的 table FetchXML,然后将查询结果转储到table。

我正在 运行 尝试此操作时遇到上述错误,但我不确定这是否可行,如有任何帮助或建议,我们将不胜感激。

这是我当前的 CLR 函数代码,这被简化到只尝试创建一个空的 table 和 return 一个带有 "Success" 或错误消息,这就是我从以下位置收到上述错误的地方:

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;

我 运行 这个命令来测试它并得到错误消息作为 return 值:

DECLARE @vchOutput nvarchar(MAX)
EXEC @vchOutput = dbo.RunFetchXML['test']
SELECT @vchOutput

我只是遗漏了一些东西还是无法从 CLR 函数写入(创建 table 并插入其中)?

函数,无论是 T-SQL 还是 SQLCLR,都不能修改服务器的状态。有一整套不能做的事情,例如任何 SET 命令、调用 NEWID() 等。查看 Create User-defined Functions (Database Engine) 的 MSDN 页面以获得 [=47= 的列表]. SQLCLR 函数的唯一区别是它们可以执行 T-SQL 存储过程,但前提是它们反过来不违反任何这些限制。

因此您可以继续使用函数,但将 SqlConnection 更改为使用 real/external 连接,因为这将被视为任何其他客户端连接并且没有此类限制。当然,这会阻止您创建本地临时 table 的能力,因为该临时 table 将在不同的会话中,并且会在函数结束后消失。

相反,将函数切换为存储过程:

  • Proc 没有这样的限制。唯一真正的缺点是它们在查询中的使用或与其结果的交互并不那么容易。
  • 这将允许您创建一个本地临时文件 table,尽管它可能在 proc 完成后不存在,因为它是在子进程中创建的,就像创建的 Dynamic SQL一个临时对象,一旦 EXEC 结束,它们就不存在了。
  • 您实际上不需要创建临时文件 table,因为存储过程可以 return 未硬编码到应用程序代码中的动态结果集。

补充说明:

  • dbo.RunFetchXML['test'] 是一种奇怪的语法,即使对于执行像存储过程这样的函数也是如此(这很好,但对于参数来说仍然是奇怪的语法)。应该是SET @vchOutput = dbo.RunFetchXML('test');

  • 这与 XML 有什么关系?如果您只是 return 将结果集发送给客户端,还有一种更简单的方法,即使用 SqlContext.Pipe.ExecuteAndSend(),您可以向远程机器。只要您不需要拦截结果来对它们做任何事情,这就可以避免定义结果集结构的需要。