.Net 代码如何检测 SQL 服务器何时决定卸载 AppDomain?
SQL 服务器有时会出于各种原因决定卸载 AppDomain,将其状态更改sys.dm_clr_appdomains
为E_APPDOMAIN_DOOMED
:
AppDomain 计划卸载,但当前有线程在其中执行。
当“计划卸载 AppDomain”或在 CLR 代码中检测此状态的方法时,CLR 代码中是否会引发事件?
如果我知道代码作为哪个 AppDomain 执行,我可以定期轮询状态sys.dm_clr_appdomains
,但这不会在SqlContext对象中公开。我不确定是否sys.dm_clr_appdomains.assembly_name
与AppDomain.FriendlyName
.
如果可能的话,我已经存储了永远运行的过程(当数据可用时,它们会将数据推送到客户端)。由于当 SQL Server 决定卸载 AppDomain 时这些线程仍在运行,因此 AppDomain 永远不会卸载,线程永远不会停止执行,来自客户端的连接永远不会关闭。
编辑:
匹配中的 and ,appdomain_id
我appdomain_name
使用以下 CLR 存储过程进行了检查:sys.dm_clr_appdomains
System.AppDomain.Id
System.AppDomain.FriendlyName
public static class Status
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetAppDomain()
{
var record = new SqlDataRecord(
new SqlMetaData("ID", SqlDbType.Int),
new SqlMetaData("FriendlyName", SqlDbType.VarChar, 386));
SqlContext.Pipe.SendResultsStart(record);
record.SetInt32(0, AppDomain.CurrentDomain.Id);
record.SetString(1, AppDomain.CurrentDomain.FriendlyName);
SqlContext.Pipe.SendResultsRow(record);
SqlContext.Pipe.SendResultsEnd();
}
}
因此,在等待下一个事件超时后轮询状态绝对是可能的,但它会留下与程序集状态的最大轮询频率相关的不希望的延迟量。
编辑2:
AppDomain.DomainUnload
更改为其创建 AppDomain 的程序集时不会引发该事件。我不知道当 Sql Server 决定卸载 AppDomain 时是否会引发它。
private static readonly WaitHandle unloadEvent = MakeUnloadEvent();
private static WaitHandle MakeUnloadEvent() {
var waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
AppDomain.CurrentDomain.DomainUnload += (sender, args) => {
waitHandle.Set();
};
return waitHandle;
}
[Microsoft.SqlServer.Server.SqlProcedure]
public static void OnUnload()
{
unloadEvent.WaitOne();
var record = new SqlDataRecord(
new SqlMetaData("Done", SqlDbType.Bit));
SqlContext.Pipe.SendResultsStart(record);
record.SetBoolean(0, true);
SqlContext.Pipe.SendResultsRow(record);
SqlContext.Pipe.SendResultsEnd();
}
运行OnUnload
sproc 然后更改程序集会导致ALTER ASSEMBLY
命令挂起或 AppDomain 在没有引发事件的情况下注定失败。