1

複数回呼び出す目的は、 に基づいてSqlDependency.Startの新しいインスタンスを作成するなど、他のアクションの前に問題がないことを確認することです。ここhttps://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.start (v=vs.110).aspx(備考セクション)に関するMicrosoftのドキュメントによると、呼び出しのように見えます複数回はまったく問題ありません:SqlCacheDependencyCommandSqlDependency.StartSqlDependency.Start

同一のパラメーター (呼び出しスレッドで同じ接続文字列と Windows 資格情報) を使用した複数の呼び出しは有効です。

しかし、実際には 2 回目の呼び出しで失敗する可能性があり (実際には成功したことはありません)、次の呼び出しのすべての試行がSqlDependency.Start失敗します ( を返すことfalseで、例外はスローされません)。

私がしたことは、最初の制限 (上記のリンクの「備考」セクションに記載) を満たす必要がありました。つまり、すべての呼び出しでSqlDependency.Start同じパラメーターを使用する必要があります (実際には、接続文字列である同じパラメーターが 1 つだけありました)。次のようになります。

//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 ...
}

そのため、Microsoft が何を述べているか (備考セクションの最初の制限) を理解するのは非常に困難です。2 つの呼び出しはまったく同じです。しかし、2 番目の呼び出しが失敗すると、その後に使用された同じ呼び出しは失敗します (つまり、複数回呼び出しを試みた場合、正常に開始する機会はありません)。

Sql Server のログを見ると、「リモート サービスが見つかりません ... 存在しないため」のようなメッセージがたくさんあることがわかります。

この問題の解決策や回避策は必要ありません。Microsoft が述べたように期待どおりに動作しない理由について説明が必要なだけですか、または Microsoft が述べたことを誤解していますか?

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()が失敗する可能性があることを明確にすることが役立つ場合があります。失敗させる 1 つの方法はAppDomain、異なる接続文字列を使用して複数回呼び出すことです。特定AppDomainの内で、接続文字列内の次のプロパティの少なくとも 1 つが以前に渡された接続文字列と異なる場合を除き、別の接続文字列を渡すと、は例外SqlDependency.Start()スローします。

  1. データベース名
  2. ユーザー名

つまり、最初に渡す接続文字列を正規化またはキャッシュしSqlDependency.Start()て、たとえば の値が異なる文字列を決して渡さないようにする必要がありますMax Pool Size。これは、単一のプロセスに対して多くのブローカー キューと接続を作成しないようにするためだと思います。さらに、ブローカ キューを実際に設定するときに、コマンドをブローカ キューに一致させようとするときに、SqlDependencyこれらの特徴的な接続文字列プロパティを使用して、使用するキューを決定する可能性があります。

ASP.NET のライフ サイクル

「ライフ サイクル イベントと Global.asax ファイル」の下にあるASP.NET アプリケーション ライフ サイクルのドキュメントから、次の点に注意してください。

このApplication_Startメソッドはインスタンス メソッドですが、アプリケーションの開始時にのみ呼び出されます。これは、アプリケーションの最初の HTTP 要求時に発生することがよくあります。ドキュメントには、具体的に次のように記載されています。

アプリケーションの開始時に静的データのみを設定する必要があります。HttpApplicationインスタンス データは、作成されたクラスの最初のインスタンスでのみ使用できるため、設定しないでください。

で初期化したものをクリーンアップするために使用する必要があるメソッドApplication_StartApplication_End. Web アプリケーションが適切に停止されると、アプリケーション クラスのインスタンスが作成され、Application_End呼び出されます。Application_Startこれは、呼び出されたものとは異なるアプリケーション クラスのインスタンスである可能性があることに注意してください。

ASP.NET のアーキテクチャのため、HttpApplication処理中の要求ごとに個別のクラス インスタンスが必要です。つまり、同時リクエストを処理するために複数のインスタンスが作成されます。ドキュメントには、パフォーマンス上の理由から、アプリケーション クラスのインスタンスフレームワークによってキャッシュされ、複数の要求に使用される可能性があることも記載されています。インスタンス レベルでアプリケーション クラスを初期化およびクリーンアップする機会を与えるために、メソッドInitDisposeメソッドを実装できます。これらのメソッドは、特定の要求に固有ではないアプリケーション クラスのインスタンス変数を構成する必要があります。ドキュメントの状態:

Init

HttpApplicationすべてのモジュールが作成された後、クラスのインスタンスごとに 1 回呼び出されます。

Dispose

アプリケーション インスタンスが破棄される前に呼び出されます。

しかし、あなたは でグローバル ステート (つまり ) を初期化し、 でグローバル ステート (つまりSqlDependency.Start())Application_Startをクリーンアップしていると述べSqlDependency.Stop()ましたDispose()。一度呼び出され、静的/グローバルを構成することApplication_Startを目的としDispose()ており、フレームワークが廃止されるアプリケーションクラスインスタンスごとに呼び出されるため (Application_End()呼び出される前に複数回発生する可能性があります)、依存関係をすばやく停止する可能性があります。 .

したがって、SqlDependency.Stop()サーバーが要求を使い果たした後に が呼び出される可能性があります。そのHttpApplication場合、 を呼び出すことによってインスタンスがクリーンアップされますDispose()。を にアタッチして実際に変更の監視を開始しようとすると、その後で失敗する可能性がありますSqlDependencySqlCommand既にサブスクライブされているコマンドが何をするかはわかりませんが、その時点で失敗する可能性があり、コードが新しい依存関係を再サブスクライブし、エラーが発生する可能性があります。これは、「リモート サービスが見つかりません」というエラーの説明である可能性があります。電話をかけるのがSqlDependency.Stop()早すぎて、頻繁に電話をかけすぎたのです。

于 2018-10-12T16:15:57.987 に答える