I think the core issue here is in your understanding of what a controller being "in use" is.
It seems as if you are imagining the following:
- The user types in your URL and the browser connects to the server.
- The server routes the request to an MVC Controller, which keeps a connection to the browser, and responds to each subsequent request using this connection.
- The user closes their browser, or navigates away - and the Controller closes down the connection, and disposes.
This is not, however, what actually happens. Web traffic is generally a series of isolated, individual request/response pairings. There is no ongoing connection between the browser and the server.
The more correct sequence is something like the following:
- The user types in your URL, and the browser connects to your server on port 80 (or 443 for https), passing in a valid HTTP request, containing a URL, HTTP verb, and other info.
- Your server uses the HTTP request's information to route the request to a specific piece of executable code (in this case, your
Controller
's specific Action
method).
- In order to execute, the server creates an Instance of your
Controller
, then fires it's Action
method, using the HTTP request's information as parameters.
- Your
Controller
code executes, and an HTTP response message is sent back to the browser.
- The
Controller
is .Dispose()
'd by the ControllerFactory
.
The issue you're experiencing is not caused by your Controller
being "active" - because that's not something Controller
s do. You are, instead, creating a Timer
within your Model
- and the Model
, therefore, continues to live on in memory, unable to be killed by GarbageCollection
. See this answer for more details.
In essence, think of it like this:
Your Controller
is acting as a factory. When you call its Index
method, it's equivalent to telling the factory "produce 1 Model
". Once the Model
rolls out of the factory, the factory is free to shut down, turn off the lights, and send all the workers home (the Controller
is .Dispose()
'd).
The Model
, however, lives on in memory. In most cases, the Model
should probably die off due to GarbageCollection
once it goes out of scope - but because you have an active Timer
inside of it, something is preventing that process from executing - and so the Model
continues to live in your server's memory, happily firing its code again and again on each timer expiration.
Note that this has nothing to do with whether or not the user is still on your web-site, or whether their browser is still open, etc. It has to do with the nature of variable scoping and GarbageCollection
on your server. You have now started a process which will continue until told to stop.
A potential solution:
In order to fix this, you might consider the following:
Create your Timer
inside of a Singleton object. Something like:
public static class ConnectionManager {
private static bool initialized = false;
private static DateTime lastStartedAt = DateTime.Now;
private static Timer timer = null;
private static void Initialize() {
if (timer == null) {
timer = new Timer() {
AutoReset = true,
};
timer.Elapsed += new ElapsedEventHandler(GetPulses);
}
}
public static void Start(int interval) {
lastStartedAt = DateTime.Now;
Initialize(); // Create a timer if necessary.
timer.Enabled = true;
timer.Interval = interval;
}
public static void Stop() {
timer.Enabled = false;
}
}
Change your Controller
to something like this:
public ActionResult Index() {
ConnectionManager.Start(700);
return View();
}
When you handle the expiration of the Timer
event, check to see how long ago the lastStartedAt
event occurred. If it is more than X
minutes, do not process, and fire ConnectionManager.Stop()
.
This will give you the ability to keep your serial activity polling on a rolling expiration. In other words - each time a user requests your Index
page, you will refresh the timer, and ensure that you are listening. After X
minutes, though, if no users have made a request - you can simply shut down the timer and the associated port.