異步控制器, 從ASP.NET MVC 2到4

前言

這篇文章主要針對即將到來的ASP.NET MVC4中的異步控制器做一個回顧和展望, 並未涉及到討論異步控制器的使用場合, 如果對異步控制器何時使用仍然糾結的同學, 這次可能會讓你們失望了, 不過關於這方面的討論和分析其實蠻多的, 大家也可以自行搜索.好了,廢話少說,上正文.

異步控制器

在MS越來越提倡異步操作的時代, ASP.NET MVC中的異步操作卻一直顯得比較落伍, 對於開發人員來說, 實現一個異步控制器往往要比普通的控制器花費更多的代碼. 這個特性是在ASP.NET MVC 2中被引入的, 之後就沒怎麼改過,直到現在, 隨着C# 5和 async/await的即將到來, 異步控制器現在已經跟普通的控制器操作代碼一樣的簡練啦. 現在(當然得等ASP.NET MVC4,.NET 4.5和C# 5正式的發佈之後), 你可以寫出下面這樣簡潔的代碼啦:

public async Task<ViewResult> FooBar() 
{
return View(await DoSomething("Some Action"));
}

怎樣? 真的是很簡單的吧~~

展望固然重要, 不過在這之前, 也請大家隨着我來回顧下一步控制器在ASP.NET MVC 2到4中的實現, 對比往往更能讓人印象深刻.

注意:下面關於ASP.NET MVC 4的例子都是基於ASP.NET MVC Developer Preview. 在正式版中, 這種實現可能會有所變化. 

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

在ASP.NET MVC 2/3 中, 要實現一個一步控制器,你將不得不實現兩個方法, 一個叫XXXAsync, 另外一個叫XXXCompleted, 同時你的控制器還要改成繼承自AsyncController, 關於這個的實現和講解已經有很多現成的例子, 這裏我就直接從MSDN上搬過來一個例子吧.

要看異步控制器, 我們首先看看同樣功能的同步實現, 大家應該都很熟悉了:

 public class PortalController: Controller
{
public ActionResult News(string city)
{
NewsService newsService = new NewsService();
ViewStringModel headlines = newsService.GetHeadlines(city);
return View(headlines);
}
}

我們再來看其異步實現方式:

public class PortalController : AsyncController
{
public void NewsAsync(string city)
{
AsyncManager.OutstandingOperations.Increment();
var newsService = new NewsService();
newsService.GetHeadlinesCompleted += (sender, e) =>
{
AsyncManager.Parameters["headlines"] = e.Value;
AsyncManager.OutstandingOperations.Decrement();
};
newsService.GetHeadlinesAsync(city);
}
public ActionResult NewsCompleted(string[] headlines)
{
return View("News", new ViewStringModel
{
NewsHeadlines = headlines
});
}
}

  看看上面的實現, 不得不承認, 相對同步控制器, 異步Action開發人員要做的工作還是要多一些的. 但在.NET4.0的大環境中, 我們也只能用這種方式來實現了.

  當然, MSDN的例子是標準的分層的實現例子, 在這個例子中,你將不得不實現自己的Service層, 如果你只是想簡單的調用異步Action, 有沒有方便的辦法呢? 答案是有的, 在.NET 4.0中,微軟帶來了Task類, 感興趣的同學可以猛擊這裏. 有了Task,如果你只是想簡單的一個函數裏面做異步操作也是可以滴: 

 

public class PortalController : AsyncController 
{
public void NewsAsync(string city)
{
AsyncManager.OutstandingOperations.Increment();
var task = Task.Factory.StartNew(() => RunThread(city));
task.ContinueWith(t =>
{
AsyncManager.Parameters["headlines"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
}

public ActionResult NewsCompleted(string[] headlines)
{
return View("News", new ViewStringModel
{
NewsHeadlines = headlines
});
}
private string RunThread(string input)
{
Thread.Sleep(5000);
return input;
}
}

當然, 代碼並沒有減少太多, 不過也算是一種不太複雜的實現, 雖然沒那麼好看, 但也不至於太難看.

  下面我們再來看看ASP.NET MVC 4中的異步控制器吧. 

ASP.NET 4 Developer preview中的異步控制器

在拋棄了對.NET 3的支持之後, ASP.NET MVC 4 徹底擁抱了Task類庫, 你不需要再蛋疼的給每個Action寫兩個方法, 也無需傻傻的手動對異步Action計數器增減了(AsyncManager.OutstandingOperations.Increment()), 現在的你只需拿起手指, 輕輕敲幾下, 其他的事情都由系統幫你完成.

public class PortalController : AsyncController 
{
public Task<ViewResult> News( string city)
{
return Task.Factory.StartNew(() => RunThread(city))
.ContinueWith(t =>
{
return View(new ViewStringModel()
{
Text = t.Result
});
});
}
private string RunThread(string input)
{
Thread.Sleep(5000);
return input;
}
}

  是不是好多了?Lamda可以讓一切更爽:

public Task<ViewResult> News(string city) {
return Task.Factory.StartNew(() => RunThread(city))
.ContinueWith(t => View(new ViewStringModel{ Text = t.Result }));
}

那麼, 是不是到這裏就要說再見了呢? 不是的, 請繼續往下看.

偉大的async/await

雖然還未到正式發佈的時候, 不過如果我們跟着微軟的目光往前更進一步, 在ASP.NET和C# 5中, 或者我們從這裏可以給.NET 4增加Async 的功能, 在有了async和await這兩個關鍵詞之後, 異步編碼就更簡單啦, 這其中也包括異步控制器的相關操作: 

public class PortalController : AsyncController {
public async Task<ViewResult> News(string city)
{
return View(new ViewStringModel()
{
Text = await NewThread(city)
});
}
private async Task<string> NewThread(string input)
{
Thread.Sleep(5000);
return input;
}
}

總結

由於有了async和await關鍵字以及Task類庫的幫助, 在可預見的未來裏, 我們操作異步控制器就可以像操作普通的控制器一樣了, 但就像其他的衆多新增的.NET特性一樣, 能力越大, 責任也就越大, 方便也往往意味着濫用. 異步控制器固然好, 但也並非每種場合都適合用它, 不恰當的使用它往往會導致服務器需要在不同的線程之間切換, 而這也帶來了更多額外的開銷. 在開發領域, 我們尤其要注意性能往往比其他任何東西都重要, 因此, 請在確實能提高性能和用戶相應的情況下使用異步控制器. 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章