我有一个 CLR 存储过程,它(除其他外)调用 TSQL 存储过程。TSQL 存储过程运行一些动态 SQL,并在运行前打印 SQL 以用于调试目的。报表中没有任何内容PRINT
显示在客户端中。当我们需要进行故障排除时,有什么好方法可以让我们看到这些命令?
示例代码:
dll中的C#代码
public static void Print_CLR()
{
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
conn.Open();
using (SqlCommand c = new SqlCommand("exec dbo.Print_TSQL", conn))
{
c.ExecuteNonQuery();
}
}
}
CLR 调用的过程
CREATE PROC DBO.Print_TSQL AS
PRINT 'WTF'
GO
--Exposing proc to SQL
CREATE PROC dbo.Print_CLR AS
EXTERNAL NAME
[CLRUtility].[CLRUtility.CLRUtility].Print_CLR
GO
--Executing, nothing is PRINTed for the client
EXEC dbo.Print_CLR
为了在 .NET 中捕获消息(来自
PRINT
或RAISERROR('', 1, 10)
喜欢它)(无论调用存储过程或即席 SQL),您需要设置一个将由SqlConnection.InfoMessage事件调用的方法。基本实现如下:
此时,您仍然需要对 Message 做一些事情。虽然有一些选择,但其中一些由于环境而具有安全隐患。与在用于 Windows 应用程序、控制台应用程序和 ASP.NET 应用程序的操作系统上运行的主 CLR 相比,SQL Server 的 CLR 主机受到高度限制。
如果您使用
static
如上所示的方法方法,那么您对 Message 所做的操作将影响PERMISSION_SET
包含此代码的程序集的值:如果您只是想将消息打印回用户,那么您可以执行以下操作,这可以在程序集中完成
SAFE
:如果要将消息写入文件,则可以执行以下操作,这可以在程序集中完成
EXTERNAL_ACCESS
:这可能会导致额外的延迟并且不值得。但另一方面,如果进程在中间失败,写入文件的内容仍然存在,因此这种方法肯定有一些好处。
但是,如果您想将消息存储在一个变量中以便稍后处理,那么您将需要一个静态类变量,因为捕获消息的方法是
static
. 例如,您可以声明private static m_Messages StringBuilder = new StringBuiler("");
. 然后,在您的CaptureMessage
方法中,您可以执行以下操作:这看起来很简单。对于 SQLCLR 之外的大多数环境,它都是如此。但是,在 SQLCLR 中使用静态变量需要程序集具有
PERMISSION_SET
ofUNSAFE
,并且最好尽可能避免这种情况。需要将程序集标记为的原因UNSAFE
是运行此代码的 AppDomain 在所有会话/SPID 之间共享。因此,静态类变量也在 Sessions 之间共享,这可能导致非常奇怪的行为。值得庆幸的是,如果您想将消息收集到实例变量,所有希望都不会丢失。为此,您需要使用匿名委托内联定义处理程序。匿名方法与代码的其余部分在同一范围内,因此它可以访问局部变量。这非常方便,因为它允许您轻松地将消息存储在具有
PERMISSION_SET
of的程序集中SAFE
(这是非常优选的)。