Where I spend time before the call to the action? - c#

ANSWER Please check your html structure.
I have controller and action.
public class HomeController
{
private Stopwatch controller_action;
public HomeController()
{
controller_action = new Stopwatch();
controller_action.Start();
}
public ActionResult Cars(KendoDataSourceRequest request)
{
controller_action.Stop();
System.Diagnostics.Debug.WriteLine(controller_action.Elapsed);
}
}
I call action Cars from popup window.
My actions:
I open popup and catch 100ms
I close popup
and I open popup and catch 200ms...
And this repeat again and again 100ms, 200, 300...
How check what happen after end constructor and before start action ?
Update
I used miniProfiler I have result:
this is only IE8 and IE9 in chrome all very well

Check out MiniProfiler. It is extremely easy to integrate to MVC, and will provide great info regarding your actions and what MVC is doing behind the scenes, directly in your application's UI.
You can also use it to add custom profiling messages, that can be automatically turned on during debugging but off for production code.

Isn't the MVC framework for .NET open source? You could download the source and find out.
The reason it keeps incrementing, though, is probably because a single instance of the controller is created, and re-used for multiple requests. No, nevermind.

Well, Stopwatch.Stop() does not reset the Elapsed interval.
So it seems like if you close the popup & reopen it you get current time difference from the first Start() call.
Maybe your method should look like:
public ActionResult Cars(KendoDataSourceRequest request)
{
controller_action.Stop();
System.Diagnostics.Debug.WriteLine(controller_action.Elapsed);
controller_action.Restart();
}

Related

Is it ok to use OnAfterRenderAsync() instead of OnParametersSetAsync() for database calls in Blazor Server?

I am reading that the recommendation is to use OnParametersSetAsync() for any async calls (such as database calls) inside a Blazor server component. The problem though is that OnParametersSetAsync() will fire twice in a default Blazor Server application, and I obviously don't want to access the database twice.
To avoid this problem, some people recommend replacing the "ServerPrerendered" render-mode in _Host.cshtml to "Server", which will avoid OnParametersSetAsync() firing twice. However, I don't like this solution, because the application will take longer to load for the user if we removing the initial static HTML phase.
So, my solution so far has been to put my database access calls inside OnAfterRenderAsync(), and to call StateHasChanged() once I am done. It looks something like this:
public partial class ExampleComponent
{
[Inject]
public IUserDataAccess UserAccess { get; set; }
[Parameter]
public string UserEmail { get; set; }
public IUser User { get; set; }
private bool _isLoading = true;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender == false)
return;
User = await UserAccess.GetOneAsync(UserEmail);
_isLoading = false;
StateHasChanged();
}
}
The database call gets made only once, because of the "if (firstRender == false)" condition.
This approach has been working well for me so far, but I suspect that there is something wrong with this way of proceeding, because the examples given online of a valid call to OnAfterRenderAsync() usually only mention JavaScript calls. I don't why that is though. Is there anything wrong with the example code I am giving above? And if this approach is not recommended for some reason, then how can we avoid the double call to the database if we use OnParametersSetAsync() (excluding the server-mode change discussed above)?
Thanks.
Everyone gets hung up on the initial double load and make huge design compromises that they don't need to. [Sorry, and please don't take this the wrong way]This is a probably a good example.
If you're using Blazor Server then, yes the SPA "Start page" gets rendered twice - once on the static first load and once when the SPA starts in the page.
But that's it. Everything after that only gets rendered once. If you are so hung up on that initial load, don't do "too much" in the landing page, make it "light".
To restate - This only happens once, every other routing component load in your SPA only happens once unless you force a re-load or hit F5.
On putting lots of stuff in OnAfterRender{Async}, in general DON'T. It's designed to handle after render activity such as JSInterop stuff. Doing things that then need a call to StateAndChanged is a bit self defeating!
All your once only Db stuff should be in OnInitialized{Async} - normally OnInitializedAsync as it will be async code. You have little control over when OnParametersSet{async} gets run: code in here gets called whenever the component is re-rendered.

Handling multiple instances of the same controller

I am currently developing an application in ASP.NET CORE 2.0
The following is the action inside my controller that get's executed when the user clicks submit button.
The following is the function that get's called the action
As a measure to prevent duplicate inside a database I have the function
IsSignedInJob(). The function works
My Problem:
Sometimes when the internet connection is slow or the server is not responding right away it is possible to click submit button more than once. When the connection is reestablished the browser (in my case Chrome) sends multiple HttpPost request to the server. In that case the functions(same function from different instances) are executed so close in time that before the change in database is made, other instances are making the same change without being aware of each other.
Is there a way to solve this problem on a server side without being to "hacky"?
Thank you
As suggested on the comments - and this is my preferred approach-, you can simply disable the button once is clicked the first time.
Another solution would be to add something to a dictionary indicating that the job has already been registered but this will probably have to use a lock as you need to make sure that only one thread can read-write at a time. A Concurrent collection won't do the trick as the problem is not whether this operation is thread-safe or not. The IsSignedInJob method you have can do this behind the scenes but I wouldn't check the database for this as the latency could be too high. Adding/removing a Key from a dictionary should be a lot faster.
Icarus's answer is great for the user experience and should be implemented. If you also need to make sure the request is only handled once on the server side you have a few options. Here is one using the ReaderWRiterLockSlim class.
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
[HttpPost]
public async SomeMethod()
{
if (cacheLock.TryEnterWriteLock(timeout));
{
try
{
// DoWork that should be very fast
}
finally
{
cacheLock.ExitWriteLock();
}
}
}
This will prevent overlapping DoWork code. It does not prevent DoWork from finishing completely, then another post executing that causes DoWork again.
If you want to prevent the post from happening twice, implement the AntiForgeryToken, then store the token in session. Something like this (haven't used session in forever) may not compile, but you should get the idea.
private const SomeMethodTokenName = "SomeMethodToken";
[HttpPost]
public async SomeMethod()
{
if (cacheLock.TryEnterWriteLock(timeout));
{
try
{
var token = Request.Form.Get["__RequestVerificationToken"].ToString();
var session = Session[SomeMethodTokenName ];
if (token == session) return;
session[SomeMethodTokenName] = token
// DoWork that should be very fast
}
finally
{
cacheLock.ExitWriteLock();
}
}
}
Not exactly perfect, two different requests could happen over and over, you could store in session the list of all used tokens for this session. There is no perfect way, because even then, someone could technically cause a OutOfMemoryException if they wanted to (to many tokens stored in session), but you get the idea.
Try not to use asynchronous processing. Remove task,await and async.

MVC razor refreshing view after timeout

I have come across an issue with my mvc razor website where it doesn't refresh properly my page after hitting a timeout.
Here's how I process the whole thing:
My application allows to go through multiple steps. Each step reloads the same view (index.cshtml), the difference being in the session variables. The session variables are used as parameters to make ajax calls to a webservice to get the content to be displayed within the page.
The user can select items displayed within the page and go to the next step. When they click next, it triggers a post (ajax to controller) that will add these items into a session variable so that the selection stays throughout the process and that the user can see all the items selected in the previous steps as he progresses.
I don't want the application to crash when accessing methods (like next, previous, save, etc.) because the session variables have expired.
The way I figured I would prevent that is by decorating my methods in the controller with an attribute that checks if a session variable (isActive) is set, and if it is not, I add a RedirectResult to my filterContext.
public class SessionExpireAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipSessionExpireAttribute), false).Any()){
return;
}
if (HttpContext.Current.Session["isActive"] == null)
{
HttpContext.Current.Session["timedOut"] = "true";
filterContext.Result = new RedirectResult("~/Session-Expired", true);
return;
}
}
}
I have set in my routes a route for this url so that it "triggers" the Index method of the controller. (which is the method that returns the view). That way, all session variables would reset, the URL is changed to "Session-Expired" and the page is reload (sending back to the first, default, step).
I tested and it does "trigger" the Index method and goes through it as intended and the Index method returns the view. The problem is, my page never gets reloaded.
Index method is:
[SkipSessionExpire]
[Themed]
public ActionResult Index()
{
// Some code.
return View();
}
So I thought maybe it would be related to caching. I tried several methods found on this wonderful website such as adding:
[OutputCache(Location = System.Web.UI.OutputCacheLocation.None)]
with no real success.
I also tried some "hack" by redirecting to another action which would load a new page (test.cshtml) instead and this page would then redirect to the original view (index.cshtml). The test page worked when I tryed calling it with URL but for some reason, it still won't load the page when redirecting to the action.
My "Next" method (which doesn't get called because it is skipped because of the attribute, which is intended):
[HttpPost]
[Themed]
public JsonResult Next(object item)
{
//Some code.
return Json((string)Session["CurrentUrl"]);
}
The attribute [SessionExpire] is applied to the Controller (so every method within the controller gets it).
I just now tried to put a timeout on the ajax call made to the controller (from the view) in hopes that after tha ajax calls timeouts, it could just redirect to the Index page. It doesn't work (nothing happens on first click on the "next button", and second click bring me one step too far because it is like I went back to first page and then clicked next once).
Also, if I simply refresh the page after the timeout (manually). It realoads the first page with all session variables cleared (which is what I want to do programmatically).
To sum it, up. I would like to know if anyone has experienced something similar or if someone can guide me towards a solution so that I can get a way to refresh the page so that it's sent back to the first (default) step. Also, if I misunderstood something, feel free to tell me! I'm still learning so I'm more than glad to hear constructive feedbacks to help me understand how all of this works.
If you feel I'm too vague or you simply need additional details to help me solve this, just let me know and I'll add whatever is needed. I did quite a bit of research but I might have missed something so if you find something that could be useful, make sure to share.
Thanks!

how to prevent double post on click, when it takes long time to save to db?

My problem :
if i have a simple post request where i just change some Voodoo items in db on edit:
[HttpPost]
[ValidateAntiForgeryToken]
[SessionStateActionFilter]
public ActionResult Edit(MonkeyJar voodooMonkey)
{
if (!this.service.EditMonkey(voodooMonkey))
return RedirectToAction("Edit",voodooMonkey);
return RedirectToAction("Index");
}
Lets say that EditMonkey takes 1.5 second to respond, while those 1.5 seconds is not over user can spam post requests to the same edit method, so only the lates edit will be saved. I want to prevent that.
I Read alot about this problem. I can ofcourse just diable submit button on submit via jquery, but isnt it a bit of hacky way of solving the problem? arent there any other solutions without disabling the button, and just skip the post request number 2...x and only take into avout the first one?
The double-click issue is typically solved in the UI, yes. If you are creating and have control over the UI, then it's definitely not 'hacky' to prevent double-clicks there using Javascript.
To me it sounds like you are wanting to prevent a situation where someone has either gone around your UI code in some way to perform additional click operations. Whether that's disabling/editing the Javascript, poor browser Javascript support, or something else.
You could do something with session state. If you only permit one edit at a time, you could do something like this (pseudocode):
[HttpPost]
[ValidateAntiForgeryToken]
[SessionStateActionFilter]
public ActionResult Edit(MonkeyJar voodooMonkey)
{
//Prevent double-submit
if (Session.IsEditActive)
{
//TODO: determine if you want to show an error, or just ignore the request
}
Session.IsEditActive = true;
try
{
if (!this.service.EditMonkey(voodooMonkey))
{
//I'm thinking you need this line here in case the Redirect does the Thread.Abort() before the finally gets called. (Is that possible? Too lazy to test. :) Probably a race condition--I'd keep it for a guaruntee)
Session.IsEditActive = false;
return RedirectToAction("Edit",voodooMonkey);
}
}
finally
{
//Ensure that we re-enable edits, even on errors.
Session.IsEditActive = false;
}
return RedirectToAction("Index");
}
Http is stateless. That said, a double post with same data is completely valid (as per the transport).
So, this leaves you with 2 options:
Client side: disable the button, and throttle the request. This is not hacky at all. Is by far the most common solution.
Server side: have some kind of monitor lock per session, so that same session cannot re-enter same block that is already executing.
Comparing both, I would prefer the client side option. is not 100% secure, but 99% can be accepted.

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. Within the Model, I have a timer which executes a function every 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.
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. 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:
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 Controllers 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.
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.
You have several options:
Override Dispose method on the controller and dispose your model with calling Pulses.Dispose()
Override OnActionExecuted method on the controller and do disposing
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)
Implement IDisposable interface in your Model and use it inside using statement
Hope this helps,
Dima
You can stop the Timer with JQuery+ MVC.
Jquery can respond to the unload event of the page like so:
$(window).unload(function() {
alert('Handler for .unload() called.');
});
You can make ajax calls to your controllers. We also need to create a property to access the timer. I'll assume you called your Timer Property MyTimer
Timer MyTimer { get; set; }
So you need to create a controller that returns void and calls the Stop() method of your Timer.
[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(
{
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. You will need to update the URL if it is something different. You could do this with pure javascript. JQuery helps to save you from writing lots of tedious browser compatibility code. (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()
Invoking ASP.NET MVC Actions from JavaScript using jQuery
JQuery Ajax()

Categories

Resources