首先,在 Windows 窗体中,您有 1 个进程,其中包含 1 个主窗体实例(我不会讨论辅助窗体,或者在创建多个主窗体实例的情况下完全可以接受的情况)。
在这里,在 ASP.NET 中,您有 1 个进程,其中存在许多“主”页面实例。有人可能会说,每个访问您的 Web 应用程序的不同用户都有一个。这是部分正确的:“主”页面实例的瞬时数量可能大于活动用户的瞬时数量(我们谈论的是不是 MVC 的 ASP.NET)。
您仍然可以在全局范围内访问内容,就像您以前在 WinForms 中所做的那样。唯一的区别是:
在 WinForms 中,因为通常主窗体是独一无二的,您可以将其用作内容的全局容器。在 ASP.NET 的情况下,您不能这样做,因为主页面不只有一个全局实例(例如,即使在同一个会话的情况下,来自同一个浏览器,刷新页面很可能会创建一个新的 Page 实例。为了测试它:实现该页面的其他隐式公共无参数构造函数并使用断点来检查它)
使处理来自许多浏览器的请求的所有不同线程都访问一些唯一的共享内存通常是危险的(特别是如果您不知道自己做得很好)。
我个人并不完全同意下面的想法,但通常情况下,初学者应该简单地用相当冗余的SELECT命令轰炸数据库。
我会稍微简化您的问题,以便能够提供一个不会轰炸数据库的简单解决方案:假设您不需要缓存。您只需要从数据库中读取大量内容并同意在重新启动 Web 应用程序之前您将永远不会再次读取它的事实。阅读后,该信息应该在 Web 应用程序的各个角落都可用。
如果是这种情况,那么将有一个简单的解决方案:
使用众所周知的“全局应用程序类”(Global.asax)及其“Application_Start”方法,以便在您的应用程序启动时收到通知(只需将其添加到您的项目中,就像您添加任何源文件一样,您可以在“添加”中找到它新项目对话框)
使用全局 HttpApplicationState 类,它像字典一样工作,并允许在 ASP.NET 应用程序中共享全局信息
像这样:
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 也崩溃但不那么频繁),您只是“重新连接 IDE 的调试器”到 Web 服务器,你会发现一切都是一样的。
更重要的是:如果您重建(强制重建,以防不需要重建)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...
}
}
编辑结束