asp.net mvc 特性 使用异步Controllers
使用asp.net异步执行请求的原因和好处,可以搜之,不赘叙。
看一个同步抓取网页图片的方法:
Public ContentResult GetPhotoByTag(string tag)
{
// Make a request to Flickr
string url = string.Format(FlickrSearchApi, HttpUtility.UrlEncode(tag));
using (var response = WebRequest.Create(url).GetResponse())
{
// Parse the response as XML
Var xmlDoc = XDocument.Load(XmlReader.Create(response.GetResponseStream()));
// Use LINQ to convert each <photo /> node to a URL string
var photoUrls = from photoNode in xmlDoc.Descendants("photo")
select string.Format(
"http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg",
photoNode.Attribute("farm").Value,
photoNode.Attribute("server").Value,
photoNode.Attribute("id").Value,
photoNode.Attribute("secret").Value);
// Return an <img> tag referencing the first photo
return Content(string.Format("<img src='{0}'/>", photoUrls.First()));
}
}
改写成异步。
实现异步Controller 3个关键点:
1,继承自AsyncController
2,方法名称格式:ActionNameAsync, ActionNameCompleted。
3,设置信号量
public class ImageController : AsyncController
{
public void GetPhotoByTagAsync(string tag)
{
AsyncManager.OutstandingOperations.Increment();
// Begin an asynchronous request to Flickr
string url = string.Format(FlickrSearchApi, HttpUtility.UrlEncode(tag));
WebRequest request = WebRequest.Create(url);
request.BeginGetResponse(asyncResult =>
{
// This lambda method will be executed when we've got a response from Flickr
using (WebResponse response = request.EndGetResponse(asyncResult))
{
// Parse response as XML, then convert to each <photo> node to a URL
var xml = XDocument.Load(XmlReader.Create(response.GetResponseStream()));
var photoUrls = from photoNode in xml.Descendants("photo")
select string.Format(
"http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg",
photoNode.Attribute("farm").Value,
photoNode.Attribute("server").Value,
photoNode.Attribute("id").Value,
photoNode.Attribute("secret").Value);
AsyncManager.Parameters["photoUrls"] = photoUrls;
// Now allow the Completed method to run
AsyncManager.OutstandingOperations.Decrement();
}
}, null);
}
public ContentResult GetPhotoByTagCompleted(IEnumerable<string> photoUrls)
{
return Content(string.Format("<img src='{0}'/>", photoUrls.First()));
}
}
使用异步三部曲:1,方法名称:XXXXXAsync,XXXXXCompleted;2,一步方法中通过信号量控制同步:逻辑开始前递增信号量AsyncManager.OutstandingOperations.Increment();逻辑后递减信号量 AsyncManager.OutstandingOperations.Decrement();3,传参数和执行结果: AsyncManager.Parameters["photoUrls"] = photoUrls;
如何给completed传参数:
public void GetPhotoByTagAsync(string tag, string someOtherParam)
{
AsyncManager.Parameters["someOtherParam"] = someOtherParam;
// ... all else as before ...
}
public ContentResult GetPhotoByTagCompleted(IEnumerable<string> photoUrls,
string someOtherParam)
{
// ...
}
通过AsyncManager.Parameters集合传键值对参数,Completed方法会试图通过参数名称去匹配参数值。
如何指定超时:
[AsyncTimeout(10000)] // 10000 milliseconds equals 10 seconds
public void GetPhotoByTagAsync(string tag) { ... }
如何终止异步操作。
这里提供的方法比较粗暴,只能用AsyncManager.Finish()把所有未完成异步调用终止。该方法导致Completed提早调用,AsyncManager.Parameters里面的值会设置为默认值(0,null..etc)。
使用Sync() 强制回调方法运行在绑定了原始 HTTPContext的asp.net工作线程上:
在control中,假如异步方法带有回调,那么该回调将不可预见在什么线程上执行。自然不能肯定的去访问HTTPContext。解决办法是使用AsyncManager.Sync。示例:
BeginAsyncOperation (asyncResult => {
var result = End (asyncResult);
// Can't always access System.Web.HttpContext.Current from here...
Action doSomethingWithHttpContext = () => {
// ... but can always access it from this delegate
};
if (asyncResult.CompletedSynchronously) // Already on an ASP.NET thread
doSomethingWithHttpContext();
else // Must switch to an ASP.NET thread
AsyncManager.Sync(doSomethingWithHttpContext);
AsyncManager.OutstandingOperations.Decrement();
}, null);