批量调用存储过程时SQLCLR状态持久化

SQLCLR state persistence when calling stored procedures in a batch

假设在 SQLCLR 存储过程之间共享状态的静态变量在存储过程执行期间不会被取消是否正确,因为存储过程是在同一个程序集中定义的,并且是在批处理中执行的 - 这使得查询优化器识别它?

静态变量确实在程序集内的任何方法执行之间保留它们的值(当然,这要求程序集被标记为 UNSAFE)。但是,这与查询优化器识别任何内容无关,因为查询优化器无法洞察程序集内部的内容。由于 App 域持续存在,静态变量保留其值直到:服务重启、由于内存压力而强制卸载、对包含 App 域的数据库进行的安全更改、对 App 域中程序集的安全更改等。静态的事实变量在 SQLCLR 对象(不仅仅是存储过程)之间共享它们的值是由于 SQLCLR 在所有会话之间共享应用程序域。

请注意,虽然静态变量 可以 在执行之间共享值,但:

  • 这与批次无关。这些值甚至可以跨查询批次共享。

  • 并不是 gua运行teed 任何值都会在 all 次执行之间始终存在,因为即使 SQL CLR 对象当前正在执行,也允许 SQL 服务器卸载应用程序域。因此,如果两个 SQLCLR 对象——可能是存储过程——一个接一个地执行,即使在同一批次中,可能 卸载 App Domain ** 在执行第二个存储过程之前(即在查询之间),因此应用程序域将再次启动以处理第二个存储过程,但没有任何先前调用的指示,因此在任何静态变量中都没有先验状态。

    意思是,如果被清除的静态变量中的值会导致后续执行 SQLCLR 对象做一些意想不到的事情,那么不要为此目的使用静态变量。它们适用于缓存,但在许多用途中可能不可靠。事实上,在 S.O 上有 another question,有人正是这样做的——在执行 UPDATE 语句和 运行 之间缓存一些值以提高性能这个问题。在那个链接的问题中,O.P。有一个主要的 SQLCLR 存储过程,它将值存储在一个静态变量中,并执行其他 SQLCLR 存储过程,这些存储过程从该静态变量中读取。主 SQLCLR 存储过程一直运行到其子调用成功完成,但在其执行期间,应用程序域被标记为卸载。那时,所有新的 SQLCLR 执行都发生在一个新的应用程序域中,即使具有主要 SQLCLR 存储过程的应用程序域仍然是 运行。但是,主 SQLCLR 存储过程的应用程序域的状态为“DOOMED”,任何新的 SQLCLR 调用都无法访问(因为任何新调用都发生在新的应用程序域中)。

如果您需要在不与其他会话共享的同一会话中的执行之间保持状态,您可以:

每个都需要使用 "Context Connection = true;" 作为连接字符串在 SQLCLR 存储过程中创建/设置。但它们应该在标量 UDF 中可读,也使用 "Context Connection = true;" 作为连接字符串。


** 由于内存压力或数据库或程序集的安全更改而被标记为卸载的应用程序域将 不会 终止当前 运行 SQLCLR 进程。应用程序域保持 运行,但在进程完成后立即卸载。

但是,如果应用程序域由于 .NET 集成/“启用 clr”而被卸载,通过 sp_configure然后 SQL CLR进程将被立即杀死。

SQL CLR 不支持写入静态变量。您的 AppDomain 可以随时拆除和重新加载,因此您不能依赖在调用之间保留静态变量状态。

存储数据的正确位置是 table 或 SESSION_CONTEXT