Aaronontheweb

Hacking .NET and Startups

How to Use Asynchronous Controllers in ASP.NET MVC2 & MVC3

January 6, 2011 12:01 by Aaronontheweb in ASP.NET // Tags: , , , , // Comments (6)

The primary reason I added asynchronous methods to Quick and Dirty Feed Parser 0.3 was because I wanted to use QD Feed Parser in conjunction with asynchronous controllers in ASP.NET MVC3.

MSDN has some excellent documentation which explains the ins and outs of asynchronous controllers in ASP.NET MVC, yet there aren’t many good examples of how to use it online. Given this, I thought I would make one.

Asynchronous Controllers in Theory

Here’s how asynchronous controllers work:

async controllers asp.net mvc

And in case my visual isn’t clear:

  1. A user sends a request to some resource on your server, i.e. GET: /controller/action
  2. Since the action method is part of an asynchronous controller, the processing of the request is handed off to a CLR thread pool worker while the IIS thread bails and serves other requests. This is the key advantage of asynchronous controllers: your limited, precious IIS threads get to off-load long-running, blocking tasks to inexpensive CLR worker threads, freeing up said IIS threads to serve more requests while work is being done in the background.
  3. The CLR worker thread diligently works away while the IIS worker thread gets recycled.
  4. The CLR worker thread finishes its task, and invokes the AsnycManager.Sync method and passes back some piece of processed data to be returned to the end-user.
  5. The AsyncManager hitches a ride on the first available IIS thread and passes along the processed data from the CLR worker thread into a special action method which returns some sort of consumable data back to the User, such as a View or a JsonResult.

Asynchronous controllers are all about utilizing additional, cheap CLR threads to free up expensive IIS threads as often as possible. In any situation where you have a long-running IO-bound task, they’re a good way to improve the performance of your ASP.NET MVC application.

Asynchronous Controllers in Code

To create an asynchronous controller, all you have to do is inherit from the AsyncController class:

public class FeedController : AsyncController
{}

For every asynchronous action method you want to add to your controller, you have to actually supply two different methods:

// GET: /Feed/

public void FeedAsync(string feeduri, 
			int itemCount){ ... }

public JsonResult FeedCompleted(IFeed feed,
			int itemCount){ ... }
			

Both of these methods on the asynchronous controller support the same action method (“Feed”) and any request that goes to “Feed/Feed” in our ASP.NET MVC application will be served asynchronously.

Asynchronous Action Naming Conventions

Both methods have to follow these naming conventions:

  1. The first method is the worker method that actually performs the long-running task on a worker thread; it should return void; and it must be named [Action Name]Async.
  2. The second method is the output method which is joined back to an IIS worker thread by the AsyncManager; it must return a valid ActionResult derivative (View, RouteResult, JsonResult, etc…;) and it must be named [Action Name]Completed.

Full Asynchronous Action Methods

Here’s full source code from the asynchronous controller I used to build Geeky Reads, minus a bit of work I put in to use object caching:

public class FeedController : AsyncController    {
protected IFeedFactory _feedfactory;

public FeedController(IFeedFactory factory){
	_feedfactory = factory;        
}

// GET: /Feed/       
public void FeedAsync(string feeduri, 
			int itemCount){            

AsyncManager.OutstandingOperations.Increment();

_feedfactory.BeginCreateFeed(new Uri(feeduri),
	async => AsyncManager.Sync(
		() => { 
		
		var feed = _feedfactory.EndCreateFeed(async);
		AsyncManager.Parameters["feed"] = feed;
		AsyncManager.Parameters["itemCount"] = itemCount;
		AsyncManager.OutstandingOperations.Decrement();
		
		}));
		
}

public JsonResult FeedCompleted(IFeed feed, int itemCount){

return Json(FeedSummarizer.SummarizeFeed(feed, itemCount), 
	JsonRequestBehavior.AllowGet);
}

}

Here’s what you should be looking at in this example:

AsyncManager.OutstandingOperations.Increment and .Decrement – the number of .Decrement operations must mach the number of .Increment operations; otherwise, the AsyncManager will time out operations that are running too long and the completion method will never return an ActionResult to the end user.

The accounting is pretty simple: you call .Increment at the beginning of your long-running operation, and .Decrement once your operation is finished and the results have been passed into the AsyncManager.Parameters collection, which brings me to my next point.

AsycManager Parameters and Argument Names for the Completion Method

Notice how the AsyncManager parameters “feed” and “itemCount” match the argument names for the FeedCompleted method – that’s not an accident. The AsyncManager binds its parameter collection to the FeedCompleted method arguments once the .Sync method completes, and this is what allows the asynchronous method results to be passed back to the consumer.

If you’d like to see the full source for this example, check it out on Github.

If you enjoyed this post, make sure you subscribe to my RSS feed!



How-To: Remote Validation in ASP.NET MVC3

ASP.NET MVC3 has been a major boon to my productivity as a web developer since I started using it at the beginning of November – the new Razor view engine has been attracting most of the attention with this iteration of MVC, but one extremely sexy feature has gone unnoticed thus far: Remote validation.

Remote validation was one of the new features added in the November Release Candidate (RC) for MVC3 – I had a chance to demo it in front of the LA .NET User Group last night and it was a monster hit.

Example:

You’ve all seen remote validation before – ever see one of these when you’re signing up for a service like Twitter?

image

That’s remote validation at work. Twitter, Facebook et al make an AJAX call to a remote service hook that checks the database to see if the username is available as the user types it. It’s a major user experience improvement as they get that feedback instantly instead of having to wait until after they fill out the rest of the form and submit it.

In the past with ASP.NET MVC you had to write your own custom jQuery scripts on top of the jQuery validation engine to achieve this end-result; in ASP.NET MVC3 this is taken care of for you automatically!

Let me show you how it works:

1. Decorate a model class with the Remote attribute

Take the class you want to validate and decorate the attributes you need to remotely validate with the Remote attribute.

public class NewUser
{
	[Remote("UserNameExists", "Account", "Username is already taken.")]
	public string UserName { get; set; }

	[Remote("EmailExists", "Account", "An account with this email address already exists.")]
	public string EmailAddress { get; set; }

	public string Password { get; set; }
}

In both of these instances of the Remote attribute, I’ve passed the following arguments:

  • The name of an action method;
  • The name of the controller where the action method lives; and
  • A default error message should user input fail this validation challenge.

2. Implement an action method to support your Remote attribute

You will need to implement an action method that supports your Remote attribute. Add an action method which returns a JsonResult to the controller you named in your Remote attribute arguments. In this example I’ll need to add these action methods to the “Account” controller:

public JsonResult UserNameExists(string username)
{
    var user = _repository.GetUserByName(username.Trim());
    return user == null ? 
		Json(true, JsonRequestBehavior.AllowGet) : 
		Json(string.Format("{0} is not available.", username),
			JsonRequestBehavior.AllowGet);
}

public JsonResult EmailExists(string emailaddress)
{
    var user = _repository.GetUserByEmail(emailaddress.Trim());
    return user == null ?
		Json(true, JsonRequestBehavior.AllowGet) : 
		Json(
			string.Format("an account for address {0} already exists.",
			emailaddress), JsonRequestBehavior.AllowGet);
}

 

If the repository returns null, meaning that a user account with this particular user name or email address doesn’t already exist, then we simply return a JsonResult with a value of true and go off on our merry way. This will suppress the jQuery validation library from raising a validation error.

If the action method returns false, then jQuery will raise a validation error and display the default error message provided in the Remote attribute arguments – if you didn’t provide a default error message yourself then the system will use an ambiguous default one.

If the action method returns a string, jQuery will raise a validation error and display the contents of the string, which is what I’m doing here in this example.

Small Gotcha – Naming Action Method Parameters

There’s one small gotcha that can be easy to miss – the name of the argument on your action method must match the name of your property on your model. If I changed the EmailExists body to look like this:

public JsonResult EmailExists(string email);

Then ASP.NET would pass a null value to this method.

Case 1: parameter name matches the name of the model’s property

image

Case 2: parameter name does not match the name of the model’s property

image

This is because the jQuery parameter takes the name of the property on the model and passes it as a querystring argument to your action method – here’s what your validation request looks like in Firebug:

[host]/account/emailexists?area=an%20account%20with%20this%20email%20address%20already%20exists.&EmailAddress=test%40test.com

The ASP.NET MVC model-binder isn’t all-knowing – it’s not going to be able to tell that EmailAddress and email are the same thing, thus it ultimately won’t bind an argument to your action method, hence why the null value is passed.

If you follow the convention of using a common name for your Remote validator action method arguments and your model properties, you won’t run into this issue.

UPDATE: Thanks to Rick Anderson for directing me to the much more extensive MSDN documentation on how to implement custom Remote validaiton in ASP.NET MVC3.

If you enjoyed this post, make sure you subscribe to my RSS feed!



LA .NET User Group ASP.NET MVC3 Lecture Notes

December 6, 2010 11:46 by Aaronontheweb in ASP.NET // Tags: , , , // Comments (3)

Putting this online for the benefit of the .NET LA User Group – this content is meant to accompany my talk on ASP.NET MVC3:

ASP.NET MVC3 links:

Source code:

PeerLearningSite on Github (Example Project with ASP.NET MVC3 RC)

Presenters:

Lastly, join the LA Windows Phone 7 Developers Group.

I’m going to put up some blog content to accompany this once my talk is done tonight – now I’ve got to go practice and give my talk :p

If you enjoyed this post, make sure you subscribe to my RSS feed!



Search

About

My name is Aaron, I'm an entrepreneur and a .NET developer who develops web, cloud, and mobile applications.

I left Microsoft recently to start my own company, MarkedUp - we provide analytics for desktop developers, focusing initially on Windows 8 developers.

You can find me on Twitter or on Github!

Recent Comments

Comment RSS

Sign in