简体   繁体   English

MVC代码以查看控制器是否仍在使用中

[英]MVC code to see whether or not a Controller is still in use

In my MVC application, I have a controller which creates an instance of a Model object. 在我的MVC应用程序中,我有一个控制器,该控制器创建Model对象的实例。 Within the Model , I have a timer which executes a function every 700ms . Model ,我有一个计时器,该计时器每700ms执行一次功能。 When I run the controller within my website, I continue to see the timer running even after I close the browser window. 当我在网站上运行控制器时,即使关闭浏览器窗口,我仍然可以看到计时器正在运行。 (The timer starts/stops a serial port connection, and I have a light that indicates when there is communication). (计时器启动/停止串行端口连接,并且我有一个指示灯,指示何时进行通讯)。

The communication actually never stops until I restart IIS entirely. 在我完全重新启动IIS之前,通信实际上永远不会停止。

Is there some way in which I can check whether the controller is still in use, and thereby stop the timer if the controller is no longer active? 有什么方法可以检查控制器是否仍在使用中,如果控制器不再处于活动状态,则可以停止计时器?

Here is the model code: 这是模型代码:

public CheckPulses(int interval) {
    CheckPulsesTimer(interval);   
}

public void CheckPulsesTimer(int interval) {
    Timer timer = new Timer();
    timer.Enabled = true;
    timer.AutoReset = true;
    timer.Interval = interval;
    timer.Elapsed += new ElapsedEventHandler(GetPulses);
}

And here is the Controller code: 这是控制器代码:

public ActionResult Index() {
    CheckPulses Pulses = new CheckPulses(700);
    return View();
}

I thought perhaps I should just add something to the GetPulses event, which happens every time the timer interval expires, and check there whether the controller is still in use. 我想也许我应该只向GetPulses事件添加一些东西,该事件在每次定时器间隔到期时发生,并检查该控制器是否仍在使用中。 If it isn't, stop the timer. 如果不是,请停止计时器。 However, I'm not sure how to write that check. 但是,我不确定如何写支票。 Can anyone point me in the right direction? 谁能指出我正确的方向?

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: 似乎您正在想象以下内容:

  1. The user types in your URL and the browser connects to the server. 用户输入您的URL,浏览器将连接到服务器。
  2. 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. 服务器将请求路由到MVC控制器,该控制器与浏览器保持连接,并使用此连接响应每个后续请求。
  3. The user closes their browser, or navigates away - and the Controller closes down the connection, and disposes. 用户关闭他们的浏览器,或导航离开-然后Controller关闭连接并进行处理。

This is not, however, what actually happens. 但是,实际情况并非如此。 Web traffic is generally a series of isolated, individual request/response pairings. Web流量通常是一系列隔离的,单独的请求/响应配对。 There is no ongoing connection between the browser and the server. 浏览器和服务器之间没有正在进行的连接。

The more correct sequence is something like the following: 更正确的顺序如下所示:

  1. 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. 用户输入您的URL,然后浏览器通过端口80(或https的443)连接到您的服务器,传入有效的HTTP请求,其中包含URL,HTTP动词和其他信息。
  2. 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). 您的服务器使用HTTP请求的信息将请求路由到特定的可执行代码段(在这种情况下,是Controller的特定Action方法)。
  3. 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. 为了执行,服务器创建了Controller的Instance,然后使用HTTP请求的信息作为参数来触发它的Action方法。
  4. Your Controller code executes, and an HTTP response message is sent back to the browser. 您的Controller代码将执行,并且HTTP响应消息将发送回浏览器。
  5. The Controller is .Dispose() 'd by the ControllerFactory . ControllerControllerFactory .Dispose()

The issue you're experiencing is not caused by your Controller being "active" - because that's not something Controller s do. 您遇到的问题不是由Controller处于“活动状态”引起的-因为那不是Controller所做的。 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 . 相反,您正在Model创建一个Timer ,因此该Model继续存在于内存中,无法被GarbageCollection杀死。 See this answer for more details. 有关更多详细信息,请参见此答案

In essence, think of it like this: 本质上,应这样考虑:

Your Controller is acting as a factory. 您的Controller正在充当工厂。 When you call its Index method, it's equivalent to telling the factory "produce 1 Model ". 当您调用其Index方法时,等效于告诉工厂“生产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). Model推出工厂后,工厂可以自由关闭,关闭灯,并将所有工人送回家( Controller.Dispose() 'd)。

The Model , however, lives on in memory. 但是, Model仍然存在于内存中。 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. 在大多数情况下,一旦超出范围,该Model应该由于GarbageCollection而消失-但由于其中有一个活动的Timer ,因此某些东西阻止了该进程的执行-因此该Model继续存在于服务器的内存中,在每次计时器到期时愉快地一次又一次地触发其代码。

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. 请注意,这无关与用户是否仍是您的网站,或者他们的浏览器是否仍处于打开状态等,与变量范围和性质做GarbageCollection的服务器上。 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: 为了解决此问题,您可以考虑以下因素:

  1. Create your Timer inside of a Singleton object. Singleton对象中创建您的Timer 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; } } 
  2. Change your Controller to something like this: 将您的Controller更改为以下内容:

     public ActionResult Index() { ConnectionManager.Start(700); return View(); } 
  3. When you handle the expiration of the Timer event, check to see how long ago the lastStartedAt event occurred. 处理Timer事件的到期时,请检查lastStartedAt事件发生多久了。 If it is more than X minutes, do not process, and fire ConnectionManager.Stop() . 如果超过X分钟,则不进行处理,并触发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. 换句话说,每次用户请求您的“ Index页面时,您将刷新计时器,并确保您正在收听。 After X minutes, though, if no users have made a request - you can simply shut down the timer and the associated port. 但是,在X分钟后,如果没有用户提出请求-您只需关闭计时器和相关的端口即可。

You are likely not stopping or disposing the Timer. 您可能没有停止或处置计时器。 This will cause it to not be garbage collected and stay active for the duration the application stays running. 这将导致它不会被垃圾回收,并在应用程序保持运行期间保持活动状态。

You should stop and dispose of the Timer within the Controller's Dispose method. 您应该在Controller的Dispose方法中停止并处置Timer。

You have several options: 您有几种选择:

  1. Override Dispose method on the controller and dispose your model with calling Pulses.Dispose() 在控制器上重写Dispose方法,并通过调用Pulses.Dispose()处置模型。
  2. Override OnActionExecuted method on the controller and do disposing 在控制器上重写OnActionExecuted方法并进行处理
  3. Create custom action filter and implement OnActionExecuted method and assign it to controller or action.(You can place reference to your model into TempData and then in your ActionFilter check if filterContext.Controller.TempData["MyReferenceToModel"] != null , then use it to dispose timer) 创建自定义动作过滤器并实现OnActionExecuted方法,并将其分配给控制器或动作。(您可以将对模型的引用放入TempData,然后在ActionFilter中检查是否filterContext.Controller.TempData [“ MyReferenceToModel”]!= null ,然后使用它配置计时器)
  4. Implement IDisposable interface in your Model and use it inside using statement 在模型中实现IDisposable接口,并在using语句中使用它

Hope this helps, Dima 希望这会有所帮助,迪玛

You can stop the Timer with JQuery+ MVC. 您可以使用JQuery + MVC停止Timer。

Jquery can respond to the unload event of the page like so: jQuery可以响应页面的unload事件,如下所示:

$(window).unload(function() {
  alert('Handler for .unload() called.');
});

You can make ajax calls to your controllers. 您可以对控制器进行ajax调用。 We also need to create a property to access the timer. 我们还需要创建一个属性来访问计时器。 I'll assume you called your Timer Property MyTimer 我假设您将计时器属性称为MyTimer

Timer MyTimer { get; set; }

So you need to create a controller that returns void and calls the Stop() method of your Timer. 因此,您需要创建一个返回void并调用Timer的Stop()方法的控制器。 [HttpPost] public void KillTimer() { MyTimer.Stop(); [HttpPost] public void KillTimer(){MyTimer.Stop(); } }

Now if you add an Ajax call to your unload event we created earlier, then we should be able to kill the timer whenever the page closes. 现在,如果将Ajax调用添加到我们先前创建的卸载事件中,那么只要页面关闭,我们就应该能够终止计时器。

$.ajax(
{
   type: "POST",
   url: "/Home/Index",
   ,
   success: function(result)
    {
        alert("Timer Closed");
    },                
   error : function(req, status, error)
   {
        alert("Sorry! We could not receive your feedback at this time.");   
   }
});

The success and error functions are not necessary. 成功和错误功能不是必需的。 I included them only for demonstration. 我包括它们只是为了演示。 I also guessed the name of your controller to be Home. 我也猜到您的控制器名称为Home。 You will need to update the URL if it is something different. 如果URL不同,则需要更新URL。 You could do this with pure javascript. 您可以使用纯JavaScript执行此操作。 JQuery helps to save you from writing lots of tedious browser compatibility code. JQuery可以帮助您避免编写大量繁琐的浏览器兼容性代码。 (I hear opera is a pain with the unload part) (我听说歌剧在卸载部分很痛苦)

To address the issue of potential browser crashes and unexpected events you can wire up your controller to check if the timer has exceeded a max time limit. 为了解决潜在的浏览器崩溃和意外事件的问题,您可以连接控制器以检查计时器是否超过了最大时间限制。

Good Luck ! 祝好运 !

Sources : 资料来源:

JQuery Unload() jQuery Unload()

Invoking ASP.NET MVC Actions from JavaScript using jQuery 使用jQuery从JavaScript调用ASP.NET MVC操作

JQuery Ajax() jQuery Ajax()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM