为了简单起见,这是我要做的一个例子:
我的CLR库用C#编写(即使我没有在示例中显示它也是线程安全的):
public static class MySP {
private static Session _session;
[Microsoft.SqlServer.Server.SqlProcedure]
[SqlFunction]
public static void Send(string destination, string message)
{
if(_session == null)
_session = new Session();
_session.SendMessage(destination, message);
}
}
编译完成后,我将其导入我的SQL Server(使用Microsoft SQL Server Management Studio):
CREATE ASSEMBLY [MyDLL] FROM 'C:\MyDLL.dll' WITH PERMISSION_SET = UNSAFE;
GO
我导入存储过程:
CREATE PROCEDURE sp_Send (@DestinationName nvarchar(256), @MessageString nvarchar(max))
AS EXTERNAL NAME MyDLL.[NS.MySP].Send
GO
最后,我有一个调用存储过程的SQL脚本:
EXEC sp_Send "MyDestination","MyMessage"
GO
现在问题: 每次我调用存储过程(如上所示)时,都会创建一个新的Session对象(我知道,因为我看到另一侧打开了多个TCP连接)。
如何阻止SQL Server加载我的库,以便它实际上强制执行“静态”对象范例?我只想要创建单个“Session”,直到SQL Server的进程终止,而不是每次调用存储过程时都是一个静态对象。
谢谢。
更多细节(不确定是否有必要): 我的“Session”对象是从另一个库加载的(所以我在技术上加载了2个DLL,但在上面的例子中只显示了一个以保持简单),它反过来包装(并加载)一个本机DLL,不确定这些信息是否相关但我想我会添加它。
编辑: 我还想补充一点,如果我在同一个SQL脚本中多次调用我的存储过程,则会创建一个Session对象。每次我用多个Stored Procedure调用调用该脚本时,都会创建一个新的Session对象。
答案 0 :(得分:1)
我从未编写过.NET存储过程,但很明显这是因为SQL服务器创建了多个AppDomain(或者在程序执行完毕后加载AppDomain并卸载它)。您应该能够在SQL服务器日志中看到AppDomain活动。
从我用谷歌搜索的小东西,SQL服务器应该只为每个数据库加载一个AppDomain。可能是您编写的代码以某种方式抛出了未处理的异常,导致AppDomain卸载(并且当再次调用该过程时服务器会重新加载它)。
SQL服务器也可能处于内存压力之下,在这种情况下,它将卸载AppDomain以节省内存。
我认为您不能保证SQL服务器中只有一个Session对象,但您可以最大限度地减少创建新会话的次数。如果您想要完全控制会话的生命周期,您必须自己托管它(可能在通过WCF公开会话的Windows服务中,并让SQL服务器连接到服务以便与会话交互)。只要SQL服务器是主机 - 您的会话遵循其规则...
(然后,可能只是因为我读过的信息是错误的,而SQL服务器的AppDomain管理是以不同的方式完成的)