Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In asp.net-mvc, how can I run an expensive operation without slowing down user experience?

Tags:

I have an asp.net-mvc website and I am using nhibernate for my ORM.

I have a current controller action that does a basic CRUD update (queries an item from the database and then updates a bunch of values and commits back to the db table). It then returns a simple json response to the client to indicate success or error.

 public ActionResult UpdateEntity(MyEntity newEntity)  {       var existingEntity = GetFromRepository(newEntity.Id);       UpdateExistingEntity(newEntity, existingEntity);       return Json(SuccessMessage);  } 

In certain cases (assuming success of commit and if certain fields are changed in my object) I now want to trigger some additional actions (like emailing a bunch of people and running some code that generates a report) but I don't want to slow down the user experience of the person that is doing the update. So my concern is that if I did this:

 public ActionResult UpdateEntity(MyEntity newEntity)  {       var existingEntity = GetFromRepository(newEntity.Id);       bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity);       if (keyFieldsHaveChanged)      {           GenerateEmails();           GenerateReports();      }     return Json(SuccessMessage);  } 

that it would be too slow for the user experience of someone updating. Is there anyway (asyngc?) to have an expensive operation get triggered off of a controller action but not have that controller action slowed down because of it?

like image 587
leora Avatar asked Jun 06 '11 10:06

leora


People also ask

What are the features of ASP.NET MVC use to reduce the time taken in developing a controller?

Scaffolding reduces the time taken to develop a controller, view, etc. in the MVC framework.

Why are ASP.NET applications slightly slower on first load?

Typically an application will always take a little extra time to load as the application domain starts up. Things helping exacerbate this could be anything from poorly written code (IE: Application_Start) to certain libraries you may be using (ORMs for example). How many modules do you have loaded?


2 Answers

I've done this before.

The most robust way would be to use Asynchronous Controller's, or better yet an independant service such as a WCF service.

But in my experience, i've just needed to do "simple", one-liner task, such as auditing or reporting, as you say.

In that example, the easy way - fire off a Task:

public ActionResult Do() {     SomethingImportantThatNeedsToBeSynchronous();      Task.Factory.StartNew(() =>      {        AuditThatTheUserShouldntCareOrWaitFor();        SomeOtherOperationTheUserDoesntCareAbout();     });      return View();  } 

That's a simple example. You can fire off as many tasks as you want, synchronize them, get notified when they finish, etc.

I've currently used the above to do Amazon S3 uploading.

like image 71
RPM1984 Avatar answered Sep 22 '22 21:09

RPM1984


If the intention is to return JSON immediately, and leave the intensive work to run asynchronously in the background without affecting the user experience, then you need to start a new background thread. AsyncController won't help you here.

There are lots of arguments about starving the thread request pool by doing this (which is true), but the counter argument is you should starve the pool because the server is busy doing work. Of course ideally you should move the work off onto another server entirely via queuing/distributed systems etc, but that's a complex solution. Unless you need to handle hundreds of request you do not need to consider this option, as it'a unlikely it would ever cause an issue. It really depends of the scalability requirements of your solution, how long the background process is going to take, and how often it is called. The simplest solution is as follows:

public ActionResult UpdateEntity(MyEntity newEntity) {     var existingEntity = GetFromRepository(newEntity.Id);     bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity);     if (keyFieldsHaveChanged)     {         ThreadPool.QueueUserWorkItem(o =>                                         {                                             GenerateEmails();                                             GenerateReports();                                         });      }     return Json(SuccessMessage); } 
like image 22
TheCodeKing Avatar answered Sep 22 '22 21:09

TheCodeKing