ASP.NET MVC 2/3中的异步控制器

在ASP.NET MVC 2/3 中, 要实现一个一步控制器,你将不得不实现两个方法, 一个叫XXXAsync, 另外一个叫XXXCompleted, 同时你的控制器还要改成继承自AsyncController, 关于这个的实现和讲解已经有很多现成的例子, 这里我就直接从MSDN上搬过来一个例子吧.

看看上面的实现, 不得不承认, 相对同步控制器, 异步Action开发人员要做的工作还是要多一些的. 但在.NET4.0的大环境中, 我们也只能用这种方式来实现了.

当然, MSDN的例子是标准的分层的实现例子, 在这个例子中,你将不得不实现自己的Service层, 如果你只是想简单的调用异步Action, 有没有方便的办法呢? 答案是有的, 在.NET 4.0中,微软带来了Task类, 感兴趣的同学可以猛击这里. 有了Task,如果你只是想简单的一个函数里面做异步操作也是可以滴:

Preparing your project…

Before you can use the AsyncController, some changes to the standard “File > New > MVC Web Application” are required. Since everything I’m talking about is in the MVC Futures assembly, first grab the Microsoft.Web.Mvc.dll at CodePlex. Next, edit Global.asax.cs and change the calls to MapRoute into MapAsyncRoute, like this:

 

 

routes.MapAsyncRoute(
“Default”,
“{controller}/{action}/{id}”,
new { controller = “Home”, action = “Index”, id = “” }
);

 

 

No need to worry: all existing synchronous controllers in your project will keep on working. The MapAsyncRoute automatically falls back to MapRoute when needed.

Next, fire up a search-and-replace on your Web.config file, replacing

 

 

<add verb=”*” path=”*.mvc” validate=”false” type=”System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/>

<add name=”MvcHttpHandler” preCondition=”integratedMode” verb=”*” path=”*.mvc” type=”System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/>

 

 

with:

 

 

<add verb=”*” path=”*.mvc” validate=”false” type=”Microsoft.Web.Mvc.MvcHttpAsyncHandler, Microsoft.Web.Mvc”/>

<add name=”MvcHttpHandler” preCondition=”integratedMode” verb=”*” path=”*.mvc” type=”Microsoft.Web.Mvc.MvcHttpAsyncHandler, Microsoft.Web.Mvc”/>

 

 

If you now inherit all your controllers from AsyncController instead of Controller, you are officially ready to begin some asynchronous ASP.NET MVC development.

Asynchronous patterns

Ok, that was a lie. We are not ready yet to start asynchronous ASP.NET MVC development. There’s a choice to make: which asynchronous pattern are we going to implement?

The AsyncController offers 3 distinct patterns to implement asynchronous action methods.. These patterns can be mixed within a single AsyncController, so you actually pick the one that is appropriate for your situation. I’m going trough all patterns in this blog post, starting with…

The IAsyncResult pattern

The IAsyncResult pattern is a well-known pattern in the .NET Framework and is well documened on MSDN. To implement this pattern, you should create two methods in your controller:

 

 

public IAsyncResult BeginIndexIAsync(AsyncCallback callback, object state) { }
public ActionResult EndIndexIAsync(IAsyncResult asyncResult) { }

 

 

Let’s implement these methods. In the BeginIndexIAsync method, we start reading a file on a separate thread:

 

 

public IAsyncResult BeginIndexIAsync(AsyncCallback callback, object state) {
// Do some lengthy I/O processing… Can be a DB call, file I/O, custom, …
FileStream fs = new FileStream(@”C:WindowsInstalling.bmp”,
FileMode.Open, FileAccess.Read, FileShare.None, 1, true);
byte[] data = new byte[1024]; // buffer

return fs.BeginRead(data, 0, data.Length, callback, fs);
}

 

 

Now, in the EndIndexIAsync action method, we can fetch the results of that operation and return a view based on that:

 

 

public ActionResult EndIndexIAsync(IAsyncResult asyncResult)
{
// Fetch the result of the lengthy operation
FileStream fs = asyncResult.AsyncState as FileStream;
int bytesRead = fs.EndRead(asyncResult);
fs.Close();

// … do something with the file contents …

// Return the view
ViewData[“Message”] = “Welcome to ASP.NET MVC!”;
ViewData[“MethodDescription”] = “This page has been rendered using an asynchronous action method (IAsyncResult pattern).”;
return View(“Index”);
}

 

 

Note that standard model binding takes place for the normal parameters of the BeginIndexIAsync method. Only filter attributes placed on the BeginIndexIAsync method are honored: placing these on the EndIndexIAsync method is of no use.

The event pattern

The event pattern is a different beast. It also consists of two methods that should be added to your controller:

 

 

public void IndexEvent() { }
public ActionResult IndexEventCompleted() { }

 

 

Let’s implement these and have a look at the details. Here’s the IndexEvent method:

 

 

public void IndexEvent() {
// Eventually pass parameters to the IndexEventCompleted action method
// … AsyncManager.Parameters[“contact”] = new Contact();

// Add an asynchronous operation
AsyncManager.OutstandingOperations.Increment();
ThreadPool.QueueUserWorkItem(o =>
{
Thread.Sleep(2000);
AsyncManager.OutstandingOperations.Decrement();
}, null);
}

 

 

We’ve just seen how you can pass a parameter to the IndexEventCompleted action method. The next thing to do is tell the AsyncManager how many outstanding operations there are. If this count becomes zero, the IndexEventCompleted  action method is called.

Next, we can consume the results just like we could do in a regular, synchronous controller:

 

 

public ActionResult IndexEventCompleted() {
// Return the view
ViewData[“Message”] = “Welcome to ASP.NET MVC!”;
ViewData[“MethodDescription”] = “This page has been rendered using an asynchronous action method (Event pattern).”;
return View(“Index”);
}

 

 

I really think this is the easiest-to-implement asynchronous pattern available in the AsyncController.

The delegate pattern

The delegate pattern is the only pattern that requires only one method in the controller. It basically is a simplified version of the IAsyncResult pattern. Here’s a sample action method, no further comments:

 

 

public ActionResult IndexDelegate()
{
// Perform asynchronous stuff
AsyncManager.RegisterTask(
callback => …,
asyncResult =>
{

});

// Return the view
ViewData[“Message”] = “Welcome to ASP.NET MVC!”;
ViewData[“MethodDescription”] = “This page has been rendered using an asynchronous action method (Delegate pattern).”;
return View(“Index”);
}

 

Leave a Comment

电子邮件地址不会被公开。 必填项已用*标注