ASP.NET MVC 4加入了async关键词,使得异步编程变得很容易,但本文不是关于这个的,而是关于另外一个我没见过的实现ASP.NET MVC异步编程的方法,我估计很多人也没见过,所以我称之为Dark Magic(不常见但可能比较强大的东东)。
先看一段代码:
public void Foo() { A(); B(); C(); }
A()、B()、C()如果是远程方法调用,且三个方法互不影响,那么Foo()方法执行完成的时间就等于三个方法执行的时间之和,因为三个方法串行执行。
这时就是异步编程大放光彩的时候了,如果采用异步编程,那么Foo()方法执行完成的时间等于A、B、C三个方法的执行时间的最大值。
直入主题,ASP.NET MVC中如何异步编程实现呢(不考虑ASP.NET MVC 4的async)?
上代码:
public class HomeController: AsyncController { public void IndexAsync() { } public ActionResult IndexCompleted(...) { } }
观察下,控制器HomeController继承自AsyncController,并且多了两个Action。第一个Action名称后缀必需为Async,在这个Action方法里执行异步操作。第二个Action以Completed结尾,表示异步操作执行完成后做的事情。
假设我们现在要实现一个获取Google RSS News和MSN RSS News的应用,代码大概如下:
public class HomeController : AsyncController { private void GetGoogleNews() { } private void GetMSNNews() { } public void IndexAsync() { } public ActionResult IndexCompleted() { } }
很明显,获取Google News和MSN News这两个任务可以异步执行。
IndexAsync的实现如下:
public void IndexAsync() { AsyncManager.OutstandingOperations.Increment(2); Task.Factory.StartNew(() => GetGoogleNews()); Task.Factory.StartNew(() => GetMSNNews()); }
有点奇特,只说下第一行代码,Increment(2)表明还有2个异步任务待执行。
GetGoogleNews和GetMSNNews的实现如下:
private void GetGoogleNews() { XmlReader reader = XmlReader.Create("http://news.google.co.in/news?pz=1&cf=all&ned=in&hl=en&output=rss"); Rss20FeedFormatter formatter = new Rss20FeedFormatter(); formatter.ReadFrom(reader); reader.Close(); AsyncManager.Parameters.Add("GoogleNews", formatter.Feed.Items); AsyncManager.OutstandingOperations.Decrement(); } private void GetMSNNews() { XmlReader reader = XmlReader.Create("http://msn.com/rss/news.aspx"); Rss20FeedFormatter formatter = new Rss20FeedFormatter(); formatter.ReadFrom(reader); reader.Close(); AsyncManager.Parameters.Add("MSNNews", formatter.Feed.Items); AsyncManager.OutstandingOperations.Decrement(); }
关注AsyncManager.Parameters.Add(...),它使用了AsyncManager对象的参数集合来保存异步任务执行完成后的结果数据。
然后AsyncManager.OutstandingOperations.Decrement()方法用来表示待执行的异步任务又完成了一个。
之后是IndexCompleted的实现:
public ActionResult IndexCompleted(IEnumerable<SyndicationItem> GoogleNews, IEnumerable<SyndicationItem> MSNNews) { List<SyndicationItem> allItems = new List<SyndicationItem>(); allItems.AddRange(GoogleNews); allItems.AddRange(MSNNews); allItems.Sort(CompareDates); ViewBag.FeedItems = allItems; return View(); }
注意这里的参数IEnumerable<SyndicationItem> GoogleNews和IEnumerable<SyndicationItem> MSNNews,它们分别对应上面AsyncManager对象的参数结合中保存的两个结果数据。
最后是前台代码实现了:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <%@ Import Namespace="System.ServiceModel.Syndication" %> <!DOCTYPE html> <html> <head runat="server"> <title>Index</title> </head> <body> <div> <table border="1" cellpadding="6" cellspacing="0"> <tr><th>Latest News from Google and MSN</th></tr> <%foreach (SyndicationItem item in ViewBag.FeedItems) {%> <tr><td><a href="<%= item.Links[0].Uri %>"><%= item.Title.Text %></a></td></tr> <%}%> </table> </div> </body> </html>
访问时输入类似下面的URL即可:
http://localhost:4078/home/index
自强不息,奋斗不止!