First of all, in Windows Forms you have 1 process inside of which you hold 1 instance of the main form (I will not discuss secondary forms, or the case which is totally acceptable where you create a multitude of instances of the main form).
Here, in ASP.NET you have 1 process inside of which there exist a number of "main" page instances.
One might say there is one for each distinct user accessing your web application.
That is partially correct: The instantaneous number of "main" page instances can be greater than that of the active users (we are talking about ASP.NET which is not MVC).
You can still access things globally, just like you used to do in WinForms. The only differences are:
In WinForms because in general the main form was unique you could use it as a global container for stuff. In ASP.NET's case you can't do that because there isn't just ONE global instance of the main page (for instance, even in the case of the same session, coming from the same browser, refreshing the page will most likely create a new Page instance. To test that: implement the otherwise implicit public parameterless constructor of that page and use a breakpoint to check that out)
It is generally dangerous (especially if you don't know what you're doing very well) to make all the different threads, which are treating requests coming from many browsers, all access some unique shared memory.
I personally do not entirely agree with the following idea, but it is generally the case that a beginner should simply bombard the database with fairly redundant SELECT commands.
I'll simplify your problem a bit just to be able to give a simple solution that doesn't bombard the database: Say you didn't need a cache. You needed just to read a bunch of stuff from the database and agree with the fact that you'll never read it again until you restart the web application. Once read the information should be available in all corners of the web application.
If that were the case, then there would be an easy solution:
Use the wellknown "Global Application Class" ( Global.asax ) and its "Application_Start" method in order to be notified when your application starts (just add it to your project as you would any source file, you'll find it in the Add New Item dialog)
Use the global HttpApplicationState class which works like a Dictionary and enables the sharing of global information within your ASP.NET application
like so:
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
}
this way you'll be able to read what you've written in the global HttpApplicationState instance from anywhere in your ASP.NET application like so:
public partial class WebForm2 : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
object obj = this.Context.Application["SOME_KEY"];
// ...etc...
}
}
About re-running your app: Most of the time, the web server (especially IIS, but also ASP.NET Development Server) doesn't stop. It starts whenever you want to "re-run" your app if it was stopped. But when you stop debugging (click the Stop button in Visual Studio) that's all you're doing. You detach from the web server's process and leave it running in peace.
When you "re-run" the app, if the web server was already running (sometime ASP.NET Development Server crashes, IIS crashes too but not that often) you are just "re-attaching your IDE's debugger" to the web server, and you discover everything is the same.
There's more to that: If you rebuild (by force, in case a rebuild was not necessary) the web server doesn't stop, but it does discard your app (which runs in an isolated AppDomain) and reloads the new assemblies and starts it again. You can see all these things if you log the Global.asax "Application_Started" method.
EDIT
Here's a safe way to have your cache (that although is optimized to allow many readers have concurrent access to some global data, will still slow things down a bit -- it's a luxury to have a cache, what can I say :)).
First write a class like this one:
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();
}
}
}
}
Then use Global.asax to create an instance of it and place it in the HttpApplicationState global instance, at some arbitrary key:
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
}
You could also create a little Helper like:
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;
}
}
}
And of course, use that accordingly wherever you need it:
public partial class WebForm2 : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
SomeRecord[] records = Helper.SomeRecords;
// ...etc...
}
}
END OF EDIT