まず、Windows フォームでは、メイン フォームのインスタンスを 1 つ保持する 1 つのプロセスがあります (セカンダリ フォームや、メイン フォームの多数のインスタンスを作成する完全に許容されるケースについては説明しません)。
ここで、ASP.NET には 1 つのプロセスがあり、その中に多数の「メイン」ページ インスタンスが存在します。Web アプリケーションにアクセスする個別のユーザーごとに 1 つずつあると言う人もいるかもしれません。これは部分的に正しいです。「メイン」ページ インスタンスの瞬間的な数は、アクティブなユーザーの数よりも多くなる可能性があります (MVC ではない ASP.NET について話しています)。
WinForms で行っていたのと同じように、引き続きグローバルにアクセスできます。唯一の違いは次のとおりです。
WinForms では、一般にメイン フォームが一意であるため、それをグローバル コンテナーとして使用できます。ASP.NET の場合、メイン ページのグローバル インスタンスが 1 つだけではないため、これを行うことはできません (たとえば、同じブラウザーからの同じセッションの場合でも、ページを更新すると、ほとんどの場合、新しい Page インスタンス. それをテストするには: そのページの暗黙的な public パラメーターなしコンストラクターを実装し、ブレークポイントを使用してそれをチェックします)
多くのブラウザからのリクエストを処理しているすべての異なるスレッドをすべて、一意の共有メモリにアクセスさせることは、一般的に危険です (特に、自分が何をしているのかよくわからない場合)。
私は個人的に次の考えに完全に同意するわけではありませんが、一般的に、初心者はかなり冗長なSELECTコマンドでデータベースを攻撃する必要があります。
データベースを攻撃しない簡単な解決策を提供できるように、問題を少し単純化します。キャッシュは必要なかったとしましょう。データベースから大量のものを読み取り、Web アプリケーションを再起動するまで二度と読み取らないという事実に同意する必要がありました。情報が読み取られると、Web アプリケーションの隅々で利用できるようになります。
その場合、簡単な解決策があります。
アプリケーションの起動時に通知を受け取るには、よく知られている「グローバル アプリケーション クラス」( Global.asax ) とその「Application_Start」メソッドを使用します (ソース ファイルと同じようにプロジェクトに追加するだけで、Add新しい項目ダイアログ)
ディクショナリのように機能し、ASP.NET アプリケーション内でグローバル情報を共有できるようにするグローバル HttpApplicationState クラスを使用します。
そのようです:
public class Global : System.Web.HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
// .. read the database here
HttpContext.Current.Application["SOME_KEY"] = "ANY OBJECT";
// .. etc
}
このようにして、ASP.NET アプリケーションのどこからでもグローバル HttpApplicationState インスタンスに記述した内容を次のように読み取ることができます。
public partial class WebForm2 : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
object obj = this.Context.Application["SOME_KEY"];
// ...etc...
}
}
アプリの再実行について: ほとんどの場合、Web サーバー (特に IIS だけでなく、ASP.NET 開発サーバーも) は停止しません。アプリが停止した場合に、アプリを「再実行」するたびに開始されます。ただし、デバッグを停止する (Visual Studio の [停止] ボタンをクリックする) と、それがすべてです。Web サーバーのプロセスから切り離し、平穏に実行したままにします。
アプリを「再実行」するとき、Web サーバーが既に実行されている場合 (ASP.NET 開発サーバーがクラッシュすることもあり、IIS もクラッシュすることがありますが、それほど頻繁ではありません)、Web サーバーに「IDE のデバッガーを再接続する」だけです。そして、すべてが同じであることを発見します。
それだけではありません: 再構築する場合 (再構築が不要な場合は強制的に)、Web サーバーは停止しませんが、(分離された AppDomain で実行される) アプリを破棄し、新しいアセンブリを再読み込みして起動しますまた。Global.asax の "Application_Started" メソッドをログに記録すると、これらすべてを確認できます。
編集
これがキャッシュを持つ安全な方法です (これは、多くのリーダーがいくつかのグローバル データに同時にアクセスできるように最適化されていますが、それでも少し遅くなります - キャッシュを持つのは贅沢なことです :))。
まず、次のようなクラスを作成します。
public sealed class SafeCache<T> {
private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private readonly Func<T> expensiveReader;
private readonly TimeSpan lease;
private DateTime lastRead;
private T data;
public SafeCache(TimeSpan lease, Func<T> expensiveReader) {
this.lease = lease;
this.expensiveReader = expensiveReader;
this.data = expensiveReader();
this.lastRead = DateTime.UtcNow;
}
public T Data {
get {
this.rwLock.EnterReadLock();
try {
if (DateTime.UtcNow - this.lastRead < this.lease)
return this.data;
} finally {
this.rwLock.ExitReadLock();
}
this.rwLock.EnterUpgradeableReadLock();
try {
if (DateTime.UtcNow - this.lastRead < this.lease)
return this.data;
else {
this.rwLock.EnterWriteLock();
try {
this.data = expensiveReader();
this.lastRead = DateTime.UtcNow;
return this.data;
} finally {
this.rwLock.ExitWriteLock();
}
}
} finally {
this.rwLock.ExitUpgradeableReadLock();
}
}
}
}
次に、Global.asax を使用してそのインスタンスを作成し、任意のキーで HttpApplicationState グローバル インスタンスに配置します。
public class Global : System.Web.HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
HttpContext.Current.Application["SOME_KEY"] = new SafeCache<SomeRecord[]> (
lease: TimeSpan.FromMinutes(10),
expensiveReader: () => {
// .. read the database here
// and return a SomeRecord[]
// (this code will be executed for the first time by the ctor of SafeCache
// and later on, with every invocation of the .Data property getter that discovers
// that 10 minutes have passed since the last refresh)
}
);
// .. etc
}
次のような小さなヘルパーを作成することもできます。
public static class Helper {
public static SomeRecord[] SomeRecords {
get {
var currentContext = HttpContext.Current;
if (null == currentContext) // return null or throw some clear Exception
var cache = currentContext.Application["SOME_KEY"] as SafeCache<SomeRecord[]>;
return cache.Data;
}
}
}
そしてもちろん、必要に応じてそれを使用してください。
public partial class WebForm2 : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
SomeRecord[] records = Helper.SomeRecords;
// ...etc...
}
}
編集の終わり