1

多次调用的目的SqlDependency.Start是确保在其他一些操作(例如SqlCacheDependency基于Command. 根据微软SqlDependency.Start在此处的文档https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.start(v=vs.110).aspx(备注部分),看起来像调用SqlDependency.Start多次完全没问题:

具有相同参数的多个调用(调用线程中的相同连接字符串和 Windows 凭据)是有效的。

但实际上,第二次调用它可能会失败(实际上它对我来说从未成功过),使所有下一次调用SqlDependency.Start失败的尝试(通过返回静默false,不抛出异常)。

我所做的应该满足第一个限制(在上面链接的备注部分中提到),即所有调用SqlDependency.Start都具有相同的参数(实际上只有一个相同的参数是连接字符串)。它看起来像这样:

//at initialization step (such as in `Application_Start()` in ASP.NET MVC)
SqlDependency.Start(myConnectionString);//this usually returns OK 
//later at the time before creating an instance of SqlCacheDependency
//I tried to call the Start method again to ensure everything is ok
var ok = SqlDependency.Start(myConnectionString);//almost always false
if(ok){
    //almost never reach here ...
}

所以很难理解微软所说的(在备注部分的第一个限制中),这两个调用完全相同。但是由于第二次调用失败,之后使用的任何相同调用仍然会失败(这意味着一旦我尝试多次调用它,就没有机会成功启动它)。

当我在 Sql Server 中看到日志时,我可以看到有很多消息说找不到远程服务......因为它不存在

我不需要解决方案或解决此问题,我只需要解释为什么它不像微软所说的那样正常工作,或者我误解了微软所说的?

4

1 回答 1

1

正如Jeroen Mostert在评论和状态文档中提到的那样SqlCommand.Start()

退货

Boolean

true如果监听器初始化成功;false如果兼容的侦听器已经存在。

正如文档中的注释所描述的那样,SqlDependency.Start()并将SqlDependency.Stop()跟踪每个调用的数量。SqlDependency.Start()如果调用次数超过调用次数,它将确保后台连接正在运行或正在设置SqlDependency.Stop()(尽管我认为如果您调用SqlDependency.Stop()的次数多于调用次数,它会丢失跟踪并重置其计数SqlDependency.Start())。

Start()错误

这可能有助于澄清SqlDependency.Start()失败的可能性。使其失败的一种方法是从AppDomain具有不同连接字符串的一个中多次调用它。如果您传入不同的连接字符串,则在特定的AppDomain,SqlDependency.Start()中将引发异常,除非连接字符串中的以下属性中的至少一个与先前传递的连接字符串不同:

  1. 数据库名称
  2. 用户名

即,您应该规范化或缓存您首先传递给的连接字符串,SqlDependency.Start()以便您永远不会向它传递一个字符串,例如,具有不同值的Max Pool Size. 我认为这样做是为了避免为单个进程创建大量代理队列和连接。此外,当它尝试将命令与代理队列匹配时,您实际上设置了一个代理队列SqlDependency,它可能会使用这些区分连接字符串属性来决定使用哪个队列。

ASP.NET 生命周期

从“生命周期事件和 Global.asax 文件”下的ASP.NET 应用程序生命周期文档中,请注意以下几点:

Application_Start方法虽然是一个实例方法,但仅在应用程序启动时调用,这通常发生在应用程序的第一个 HTTP 请求期间。该文档特别指出:

您应该在应用程序启动期间仅设置静态数据。不要设置任何实例数据,因为它只对HttpApplication创建的类的第一个实例可用。

你应该用来清理你初始化的东西的方法Application_StartApplication_End. 当 webapp 正常停止时,将创建应用程序类的实例并Application_End在其上调用。请注意,这可能是应用程序类的不同实例,而不是Application_Start被调用的实例。

由于 ASP.NET 的体系结构,HttpApplication每个正在处理的请求都需要一个不同的类实例。这意味着将创建多个实例来处理并发请求。文档还指出,出于性能原因,应用程序类实例可能会被框架缓存并用于多个请求。为了让您有机会在实例级别初始化和清理应用程序类,您可以实现InitDispose方法。这些方法应该配置不特定于特定请求的应用程序类的实例变量。文档状态:

Init

HttpApplication在创建所有模块后,为类的每个实例调用一次。

Dispose

在应用程序实例被销毁之前调用。

但是,您提到您正在初始化全局状态(即SqlDependency.Start()Application_Start并清理全局状态(即SqlDependency.Stop()Dispose()。由于Application_Start将被调用一次并且旨在配置静态/全局变量并且Dispose()为框架退休的每个应用程序类实例调用(在调用之前可能发生多次Application_End()),因此您很可能正在快速停止依赖项.

因此,它可能是SqlDependency.Stop()在服务器用完请求后调用的,在这种情况下,它会HttpApplication通过调用Dispose(). SqlDependency此后,任何通过将 an 附加到 an来实际开始监视更改的尝试SqlCommand都可能会失败。我不确定已经订阅的命令会做什么,但它们可能会在那时失败,这会触发您的代码重新订阅一个新的依赖项,然后应该会遇到错误。这可能是您“找不到远程服务”错误的原因——您打电话SqlDependency.Stop()太早而且太频繁了。

于 2018-10-12T16:15:57.987 回答