精通ASP.NET MVC ——控制器可扩展性
MVC中引導(dǎo)動(dòng)作方法執(zhí)行過程的請(qǐng)求流程管道中,有兩個(gè)重要的部件:控制器工廠(Controller Factory) 和 動(dòng)作調(diào)用器(Action Invoker)。控制器工廠負(fù)責(zé)創(chuàng)建對(duì)請(qǐng)求進(jìn)行服務(wù)的控制器實(shí)例,動(dòng)作調(diào)用器負(fù)責(zé)查找并調(diào)用控制器類中的動(dòng)作方法。MVC框架中含有這兩個(gè)組件的默認(rèn)實(shí)現(xiàn),可以配置并控制他們的行為,也可以完全替換這些組件。
準(zhǔn)備示例項(xiàng)目
新建一個(gè)空的MVC項(xiàng)目名叫ControllerExtensibility的項(xiàng)目。在Model中添加一個(gè)Result.cs文件,代碼如下圖所示:
namespace ControllerExtensibility.Models {public class Result{public string ControllerName { get; set; }public string ActionName { get; set; }} }在/Views/Shared文件夾下,添加一個(gè)名稱為Result.cshtml的視圖?,代碼如下:
@model ControllerExtensibility.Models.Result @{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>Result</title> </head> <body><div>Controller:@Model.ControllerName</div><div>Action:@Model.ActionName</div> </body> </html>新增兩個(gè)控制器, Product控制器和Customer控制器,代碼如下圖所示:
public class CustomerController : Controller{// GET: Customerpublic ActionResult Index(){return View("Result",new Result { ControllerName = "Customer",ActionName = "Index" });}public ViewResult List(){return View("Result", new Result { ControllerName = "Customer",ActionName = "Index" });}} public class ProductController : Controller{// GET: Productpublic ActionResult Index(){return View("Result",new Result { ControllerName = "Product",ActionName = "Index" } );}public ViewResult List(){return View("Result", new Result { ControllerName = "Product", ActionName = "List" });}}這些控制器不執(zhí)行任何有用的動(dòng)作,只是通過Result.cshtml視圖報(bào)告他們已經(jīng)被調(diào)用了。?
創(chuàng)建自定義控制器工廠?
控制器工廠是由IControllerFactory接口定義的,如下圖所示:
public interface IControllerFactory{IController CreateController(RequestContext requestContext, string controllerName);SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);void ReleaseController(IController controller);}這個(gè)接口中最重要的方法是CreateController,當(dāng)MVC框架需要控制器對(duì)請(qǐng)求進(jìn)行服務(wù)時(shí),便會(huì)調(diào)用這個(gè)方法。該方法的一個(gè)參數(shù)是一個(gè)RequestContext對(duì)象,它讓工廠能夠檢測(cè)請(qǐng)求的細(xì)節(jié),另一個(gè)參數(shù)是一個(gè)字符串,它包含了從路由的URL那里得到所得到的controller值。
GetControllerSessionBehavior 方法由MVC框架用來確定是否應(yīng)該為控制器維護(hù)會(huì)話數(shù)據(jù)。
當(dāng)不在需要CreateController,方法創(chuàng)建的控制器對(duì)象時(shí),會(huì)調(diào)用ReleaseController方法釋放資源。
下面簡(jiǎn)單創(chuàng)建了一個(gè)控制器工廠,代碼如下:
public class CustomControllerFactory : IControllerFactory{public IController CreateController(RequestContext requestContext, string controllerName){Type targetType = null;switch (controllerName){case "Product":targetType = typeof(ProductController);break;case "Customer":targetType = typeof(CustomerController);break;default:requestContext.RouteData.Values["controller"] = "Product";targetType = typeof(ProductController);break;}return targetType == null ? null : (IController)DependencyResolver.Current.GetService(targetType);}public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName){return SessionStateBehavior.Default;}public void ReleaseController(IController controller){IDisposable disposable = controller as IDisposable;if (disposable != null){disposable.Dispose();}}}以上自定義控制器工廠只會(huì)指向?名叫Product 和 Customer 控制器,并且如果控制器不是這兩個(gè),就默認(rèn)指向Product控制器。
靜態(tài)的DependencyResolver.Current屬性返回IDependencyResolver接口的實(shí)現(xiàn)。該接口定義了GetService方法,為方法傳遞了一個(gè)System.Type對(duì)象。這里可以理解為實(shí)例化一個(gè)目標(biāo)類型對(duì)象。
注冊(cè)使用自定義控制器工廠
通過ControllerBuilder類,可以告訴MVC框架使用這個(gè)自定義的控制器工廠。在Global.asax.cs文件中的Application_Start方法中加入如下代碼即可:
protected void Application_Start() {AreaRegistration.RegisterAllAreas();RouteConfig.RegisterRoutes(RouteTable.Routes);ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());//注冊(cè) }一旦注冊(cè)了控制器工廠,將由它負(fù)責(zé)處理請(qǐng)求應(yīng)用程序接收到的所有請(qǐng)求,啟動(dòng)程序,就可以看到如下結(jié)果:?
? ? ? ? ? ??
?使用內(nèi)建的控制器工廠
對(duì)于大多數(shù)程序應(yīng)用程序,內(nèi)建的控制器工廠類DefaultControllerFactory完全足夠滿足需求。當(dāng)它從路由系統(tǒng)接收到一個(gè)請(qǐng)求時(shí),該工廠考察路由數(shù)據(jù),找到 Controller 屬性的值,并企圖在這個(gè)Web 應(yīng)用程序中找到滿足如下條件的類:
1、這個(gè)類必須是一個(gè)public類。
2、這個(gè)類必須是具體類(不是抽象類)。
3、這個(gè)類必須沒有泛型參數(shù)。
4、這個(gè)類必須以Contoller結(jié)尾。
5、這個(gè)類必須實(shí)現(xiàn)IController接口。
DefaultControllerFactory類里有這些類的一個(gè)列表,一個(gè)請(qǐng)求到達(dá)時(shí),它并不需要每次都執(zhí)行一次搜索。如果找到了,便用控制器激活器(Controller Activator)創(chuàng)建一個(gè)實(shí)例。
命名空間優(yōu)先排序
如果有同名控制器位于不同命名空間的,需要對(duì)命名空間優(yōu)先排序,在Global.asax.cs文件中的Application_Start方法中加入如下代碼即可:
ControllerBuilder.Current.DefaultNamespaces.Add("MyControllerNamespace"); ControllerBuilder.Current.DefaultNamespaces.Add("MyProject.*");所有添加命名空間的順序,并不暗示搜索順序或者相對(duì)優(yōu)先級(jí)——?所有Add方法定義的命名空間一視同仁。而優(yōu)先級(jí)是相對(duì)于那些沒有Add的方法。如果控制器在Add方法中定義的命名空間找不到合適的控制器,那就會(huì)搜搜整個(gè)應(yīng)用程序。
上述代碼中第二句的“ * ”表示的是查詢MyProject命名空間及所包含的子命名空間。
定制DefaultControllerFactory的控制器實(shí)例化
也可以通過創(chuàng)建一個(gè)控制器激活器(Controller Activator)對(duì)一個(gè)指定一個(gè)控制器類型進(jìn)行實(shí)例化。代碼如下圖所示:
public class CustomerControllerActivator : IControllerActivator{public IController Create(RequestContext requestContext, Type controllerType){if (controllerType == typeof(ProductController)){controllerType = typeof(CustomerController);}return (IController)DependencyResolver.Current.GetService(controllerType);}}此IControllerActivator的實(shí)現(xiàn)很簡(jiǎn)單——如果請(qǐng)求的是ProductController類,將以CustomerController類的實(shí)例作為其響應(yīng)。?
為了激活這個(gè)自定義控制器,也需要在在Global.asax.cs文件中的Application_Start方法中加入如下代碼:
ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new CustomerControllerActivator()));啟動(dòng)程序并導(dǎo)航到/Product,效果如下圖所示:?
? ? ? ? ? ??
?重寫DefaultControllerFactory方法
可以重寫DefaultControllerFactory類中的方法,來控制控制器的創(chuàng)建。
| 方法 | 結(jié)果 | 描述 |
| CreateController | IController | IControllerFactory接口的CreatController方法的實(shí)現(xiàn)。默認(rèn)情況下,這個(gè)方法調(diào)用GetControllerType來確定應(yīng)該實(shí)例化哪個(gè)類型,然后通過將結(jié)果傳遞給GetControllerInstance方法,來獲得一個(gè)控制器對(duì)象。 |
| GetControllerType | Type | 將請(qǐng)求映射到控制器類型。 |
| GetControllerInstance | IController | 創(chuàng)建指定類型的一個(gè)實(shí)例。 |
?創(chuàng)建自定義動(dòng)作調(diào)用器
一旦控制器工廠創(chuàng)建了一個(gè)(控制器)類的實(shí)例,框架就需要一種辦法來調(diào)用這個(gè)實(shí)例上的一個(gè)動(dòng)作。如果控制器是通過Controller類派生的,那么將由動(dòng)作調(diào)用器(Action Invoker)調(diào)用動(dòng)作。
動(dòng)作調(diào)用器實(shí)現(xiàn)IActionInvoke接口,如下圖所示:
public interface IActionInvoker{bool InvokeAction(ControllerContext controllerContext, string actionName);}該接口只有一個(gè)單一的成員:InvokeAction(調(diào)用動(dòng)作)?。其返回值是一個(gè)布爾類型的值,返回true,表示找到并調(diào)用了這個(gè)動(dòng)作方法;false表示控制器沒有匹配的動(dòng)作。
新增一個(gè)CustomActionInvoker.cs文件繼承此接口,代碼如下圖所示:
public class CustomActionInvoker : IActionInvoker{public bool InvokeAction(ControllerContext controllerContext, string actionName){if (actionName == "Index"){controllerContext.HttpContext.Response.Write("This is output from the Index action");return true;}else{return false;}}}這個(gè)動(dòng)作方法并不關(guān)心控制器類中的方法。它只處理自己的動(dòng)作。如果這是對(duì)Index動(dòng)作的請(qǐng)求,那么該調(diào)用器直接將一條消息寫到Response。如果是對(duì)其他動(dòng)作的請(qǐng)求,則返回false,這將會(huì)導(dǎo)致一個(gè)“404——未找到”的錯(cuò)誤消息給用戶。
與一個(gè)控制器相關(guān)聯(lián)的動(dòng)作調(diào)用器是通過Controller.ActionInvoker屬性獲得的,同一個(gè)應(yīng)用程序中的不同控制器可以試用版不同的動(dòng)作調(diào)用器。新增一個(gè) ActionInvoker的新控制器,代碼如下:
public class ActionInvokerController : Controller{public ActionInvokerController() {this.ActionInvoker = new CustomActionInvoker();}}這個(gè)控制器中沒有動(dòng)作方法,它依靠動(dòng)作調(diào)用器去處理請(qǐng)求。通過啟動(dòng)程序,并導(dǎo)航到/ActionInvoker/Index,可以看到其工作情況,而導(dǎo)航同一個(gè)控制器中的其他方法則看到404錯(cuò)誤。?如下圖所示:
? ? ? ? ? ? ? ??
使用內(nèi)建的動(dòng)作調(diào)用器?
內(nèi)建的動(dòng)作調(diào)用器ControllerActionInvoker類,有一些將請(qǐng)求與動(dòng)作方法進(jìn)行匹配的非常完善的技術(shù)。默認(rèn)的動(dòng)作調(diào)用器是依靠方法進(jìn)行操作的。為了具備一個(gè)動(dòng)作的資格,一個(gè)方法必須滿足如下幾個(gè)條件:
1、該方法是必須是public的。
2、該方法必須不是staticd的。
3、該方法必須不是在 System.Web.Mvc.Controller或它的任何基類中。
4、該方法沒有專用名。
前兩個(gè)條件很簡(jiǎn)單。第三個(gè)條件排除了Controller類或其基類出現(xiàn)的方法,這意味著不包括ToString及GetHashCode這樣的方法,因?yàn)檫@些都是IController接口實(shí)現(xiàn)的方法。最后一個(gè)條件意味著排除了構(gòu)造器、屬性以及事件訪問器。
注:具有泛型參數(shù)的方法(如 MyMethod<T>() 滿足所有條件,但是如果視圖調(diào)用這樣的方法吹里一個(gè)請(qǐng)求,MVC框架會(huì)報(bào)錯(cuò))。
默認(rèn)情況下,ControllerActionInvoker 查找一個(gè)具有與請(qǐng)求的動(dòng)作同名的方法。?而且,MVC框架提供了一些可以調(diào)整的方法。
使用自定義動(dòng)作名
通常,動(dòng)作方法的名稱確定了它所表示的動(dòng)作。Index動(dòng)作方法對(duì)Index動(dòng)作進(jìn)行服務(wù)。但是可以用ActionName注解屬性來重寫這一行為。如下圖所示:
public class CustomerController : Controller{// GET: Customerpublic ActionResult Index(){return View("Result",new Result { ControllerName = "Customer",ActionName = "Index" });}[ActionName("Enumerate")]public ViewResult List(){return View("Result", new Result { ControllerName = "Customer",ActionName = "Index" });}}導(dǎo)航到/Customer/Enumerata,效果如下圖所示:?
? ? ? ? ? ? ??
這一注解屬性重寫了動(dòng)作的名稱,這意味著導(dǎo)航到List方法不再工作,如下圖所示:
? ? ? ? ? ??
以這種方式重寫方法名的原因主要有兩個(gè):
1、可以接收一個(gè)作為C# 方法名不合法的動(dòng)作名,例如【ActionName(“User-Registration”】,其中“-”符號(hào)在C#中是不合法的。
2、如果希望有兩個(gè)不同的C#方法接受同一組參數(shù),并且運(yùn)用同樣的動(dòng)作名(具有同樣參數(shù)的方法不能實(shí)現(xiàn)重載,只能采用不同的方法名),但是要對(duì)不同的HTTP請(qǐng)求類型進(jìn)行響應(yīng),例如一個(gè)是【HttpGet】,而另一個(gè)是【HttpPost】那么可以對(duì)這些方法用不同的C#名來滿足編譯器的要求,然后用【ActionName】將他們映射到同一個(gè)動(dòng)作名。
使用動(dòng)作方法選擇?
很多情況是一個(gè)控制器中含有幾個(gè)同名的動(dòng)作,這可能是因?yàn)橛卸鄠€(gè)方法,每個(gè)方法的參數(shù)個(gè)數(shù)不同。或者是使用[ActionName]注解屬性,使多個(gè)方法表示同一個(gè)動(dòng)作。
動(dòng)作調(diào)用器在選擇一個(gè)動(dòng)作時(shí),會(huì)利用動(dòng)作方法選擇器來消除不確定性。比如【HttPost】注解屬性就是一個(gè)動(dòng)作方法的選擇器。首先會(huì)評(píng)估帶動(dòng)作方法選擇器的動(dòng)作,以考察其是否適合處理該請(qǐng)求。
【HttpGet】用于Get請(qǐng)求,【HttpPost】用于Post請(qǐng)求,另一個(gè)內(nèi)建的注解屬性是NonAction(非動(dòng)作),它向動(dòng)作調(diào)用器解釋不應(yīng)該作為作為動(dòng)作方法來使用。如下圖所示:
[NonAction]public ActionResult MyAction(){return View();}上述代碼中的MyAction方法,將不會(huì)被看成是一個(gè)動(dòng)作。以NonAction方法為目標(biāo)的URL請(qǐng)求會(huì)生成“404——未找到”錯(cuò)誤。另一個(gè)通常的方法是把這些方法標(biāo)記為Private。?
創(chuàng)建自定義動(dòng)作方法選擇器
動(dòng)作方法選擇器派生于ActionMethodSelectorAttribute類,如下圖所示:
//// 摘要:// 表示一個(gè)特性,該特性用于影響操作方法的選擇。[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]public abstract class ActionMethodSelectorAttribute : Attribute{//// 摘要:// 初始化 System.Web.Mvc.ActionMethodSelectorAttribute 類的新實(shí)例。protected ActionMethodSelectorAttribute();//// 摘要:// 確定操作方法選擇對(duì)指定的控制器上下文是否有效。//// 參數(shù):// controllerContext:// 控制器上下文。//// methodInfo:// 有關(guān)操作方法的信息。//// 返回結(jié)果:// 如果操作方法選擇對(duì)指定的控制器上下文有效,則為 true;否則為 false。public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo);}ActionMethodSelectorAttribute是一個(gè)抽象類,它定義了一個(gè)抽象方法:IsValidForRequest。該方法的一個(gè)參數(shù)是controllercontext對(duì)象,用來對(duì)請(qǐng)求進(jìn)行檢測(cè),另一個(gè)參數(shù)是MethodInfo對(duì)象,用來獲取運(yùn)用了選擇器方法的信息。如果該方法能處理請(qǐng)求,便返回true,否則便返回false。
如下圖中創(chuàng)建了一個(gè)簡(jiǎn)單的自定義選擇器,代碼如下圖所示:
public class LocalAttribute : ActionMethodSelectorAttribute{public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo){return controllerContext.HttpContext.Request.IsLocal;}}將該動(dòng)作方法選擇器運(yùn)運(yùn)用,創(chuàng)建了Home控制器,代碼如下圖所示:?
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){return View("Result",new Result { ControllerName = "Home",ActionName = "Index"});}[ActionName("Index")]public ActionResult LocalIndex(){return View("Result", new Result { ControllerName = "Home", ActionName = "LocalIndex" });}}上述代碼創(chuàng)建了兩個(gè)Index動(dòng)作方法,因此,當(dāng)/Home/Index請(qǐng)求達(dá)到時(shí),動(dòng)作調(diào)用器無法猜出應(yīng)該使用哪一個(gè),就會(huì)報(bào)錯(cuò):?
對(duì)Home控制器運(yùn)用注解屬性:
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){return View("Result",new Result { ControllerName = "Home",ActionName = "Index"});}[Local][ActionName("Index")]public ActionResult LocalIndex(){return View("Result", new Result { ControllerName = "Home", ActionName = "LocalIndex" });}}如果重啟程序,并從本地機(jī)器上運(yùn)行瀏覽器導(dǎo)航到根URL,將會(huì)看到MVC框架已經(jīng)考慮了方法的選擇注解屬性。解決了控制器類中方法之間的歧義問題:?
? ? ? ? ? ? ? ? ??
處理未知?jiǎng)幼?
如果動(dòng)作方法調(diào)用器找不到一個(gè)要調(diào)用的動(dòng)作方法,便從它的InvokerAction方法返回false,當(dāng)這種情況發(fā)生時(shí),Controller類會(huì)調(diào)用它的HandleUnknowAction方法,默認(rèn)情況下,這個(gè)方法會(huì)將一個(gè)“404——未找到”響應(yīng)給客戶端。這是控制器大多數(shù)應(yīng)用程序所能做的最有用的事情。如果想做一些特殊的事情,可以在控制器類中選擇重寫這個(gè)方法。代碼如下圖所示:
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){return View("Result",new Result { ControllerName = "Home",ActionName = "Index"});}[Local][ActionName("Index")]public ActionResult LocalIndex(){return View("Result", new Result { ControllerName = "Home", ActionName = "LocalIndex" });}protected override void HandleUnknownAction(string actionName){Response.Write(string.Format("You requested the {0} action ",actionName));}}導(dǎo)航到一個(gè)不存在動(dòng)作,如下圖所示:?
? ? ? ? ??
使用無會(huì)話控制器?
默認(rèn)情況下,控制器是支持會(huì)話狀態(tài)的,這可以用來跨請(qǐng)求地存取數(shù)據(jù)值,使MVC程序員的工作更輕松。創(chuàng)建和維護(hù)會(huì)話狀態(tài)是一個(gè)棘手的過程。必須對(duì)數(shù)據(jù)進(jìn)行存儲(chǔ)和接收,且必須對(duì)會(huì)話進(jìn)行管理,以使他們能適當(dāng)?shù)亟K止。會(huì)話數(shù)據(jù)會(huì)消耗服務(wù)器內(nèi)存或一些其他存儲(chǔ)單元空間。而且多個(gè)Web服務(wù)器之間的數(shù)據(jù)同步的需求,使得在服務(wù)器場(chǎng)(server farm)上運(yùn)行應(yīng)用程序更加困難。
為了簡(jiǎn)化會(huì)話狀態(tài),ASP.NET 對(duì)一個(gè)給定的會(huì)話在某一個(gè)時(shí)刻只處理一個(gè)查詢,如果客戶形成了多個(gè)重疊的請(qǐng)求,它們將被排成隊(duì)列,并由服務(wù)器依次處理。其好處是不需要擔(dān)心多個(gè)請(qǐng)求對(duì)同一數(shù)據(jù)進(jìn)行修改的情況,缺點(diǎn)是得不到所希望的請(qǐng)求的吞吐量。
并非所有控制器都需要這種會(huì)話狀態(tài)特性。在這種情況下,能夠改善應(yīng)用程序的性能,而又避免了棘手的會(huì)話狀態(tài)維護(hù)工作。這可以通過無會(huì)話控制器來實(shí)現(xiàn)。它們與規(guī)則控制器一樣,但是有兩個(gè)方面不同:在把它們用于處理一個(gè)請(qǐng)求時(shí),MVC框架不加載或不存儲(chǔ)會(huì)話狀態(tài),重疊請(qǐng)求可以同時(shí)處理。
IControllerFactory接口中,含有一個(gè)叫做“SessionStateBehavior”的方法,該方法返回SessionStateBehavior枚舉中的一個(gè)值。如下圖所示:
| 值 | SessionStateBehavior枚舉的值 |
| Default | 使用默認(rèn)的ASP.NET行為,它會(huì)根據(jù)HttpContext來決定會(huì)話狀態(tài)的配置 |
| Required | 啟用完全會(huì)話狀態(tài) |
| ReadOnly | 啟用只讀會(huì)話狀態(tài) |
| Disable | 完全禁用會(huì)話狀態(tài) |
通過返回GetControllerSessionBehavior方法的SessionStateBehavior的值,實(shí)現(xiàn)IControllerFactory接口的控制器工廠會(huì)直接設(shè)置控制器會(huì)話狀態(tài)的行為。?傳遞給這個(gè)方法的參數(shù)是RequestContext 和一個(gè)含有控制器名稱的字符串,可以返回如上圖中任意一個(gè)值,也可以根據(jù)不同的控制器返回不同的值,如下圖所示:
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) {switch (controllerName){case "Home":return SessionStateBehavior.ReadOnly;case "Prouduct":return SessionStateBehavior.Required;default:return SessionStateBehavior.Default;} }?用DefaultControllerFactory管理會(huì)話狀態(tài)
當(dāng)使用內(nèi)建的控制器工廠(MVC 應(yīng)用程序默認(rèn)使用的就是這個(gè)默認(rèn)的控制器工廠DefaultControllerFactroy)時(shí),可以將SessionState注解屬性運(yùn)用于每個(gè)控制器類,以便對(duì)控制器的會(huì)話進(jìn)行控制,如下圖所示:
[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)] public class FastController : Controller {// GET: Fastpublic ActionResult Index(){return View("Result", new Result { ControllerName = "Fast",ActionName = "Index"});} }上述控制器運(yùn)用了SessionState注解屬性,它影響著該控制器的所有動(dòng)作。?Disable完全禁用了會(huì)話狀態(tài),如果在控制中設(shè)置了一個(gè)會(huì)話值:
Session[" Message "]? = " Hello "
如果想從其他地方試圖讀取這個(gè)值,@Session[" Message "] ,MVC框架會(huì)報(bào)錯(cuò)。HttpContext.Session屬性會(huì)返回Null。
如果制定了Readonly,那么可以讀取從其他控制器設(shè)置的值,但是企圖修改,也會(huì)報(bào)錯(cuò)。
使用異步控制器?
核心ASP.NET 平臺(tái)維護(hù)著一個(gè)用來處理客戶端請(qǐng)求的.NET 線程池。這個(gè)線程池叫做“工作線程池(Work Thread Pool)”,而這些線程叫做“工作線程(Work Thread)”。當(dāng)接受到一個(gè)請(qǐng)求時(shí),將占用線程池中的一個(gè)工作線程,以進(jìn)行這個(gè)請(qǐng)求的處理工作。當(dāng)請(qǐng)求處理完成后,該工作線程被返回給線程池,以便用于新請(qǐng)求的處。對(duì)ASP.NET應(yīng)用程序使用線程池有兩個(gè)好處:
1、通過重用工作線程,避免了每次處理一個(gè)請(qǐng)求時(shí),都要?jiǎng)?chuàng)建一個(gè)新的線程的開銷(創(chuàng)建線程是需要時(shí)間的,若采用現(xiàn)有的線程就不一樣了)。
2、通過具有固定數(shù)目的可用工作線程,避免了超出服務(wù)器處理能力的并發(fā)請(qǐng)求情況。
在請(qǐng)求可以被短時(shí)間處理完畢的情況下,工作線程池工作的最好。這也是大多數(shù)MVC應(yīng)用程序的情況。但是,如果有一些依賴于其他的服務(wù)器且占用較長(zhǎng)時(shí)間才能完成的動(dòng)作,那么你可能會(huì)遇到所有工作線程都被綁定于等待其他系統(tǒng)完成其工作的情況。
此刻服務(wù)器有能力做更多的工作——畢竟,這只是在等待,只占用了很少的資源——但是因?yàn)樗芯€程都被綁定,傳入的請(qǐng)求都被排成隊(duì)列。這將陷入應(yīng)用程序處理停頓,而服務(wù)器大片的閑置的奇怪狀態(tài)。
這一問題的解決方案是使用異步控制器,這是提高應(yīng)用程序的整體性能,但是不利于執(zhí)行異步操作(即可提高性能,但實(shí)現(xiàn)(異步操作)難)。
注意:編寫并發(fā)代碼容易,編寫能夠正常工作的并發(fā)代碼是及其困難的。最好使用默認(rèn)的線程池。特別是對(duì)于新手。即便是老手,也應(yīng)該知道,編寫和測(cè)試一個(gè)新的線程池所付出的努力,與得到的回報(bào)是相比,是微不足道的。
異步控制器只能對(duì)占用I / O 或占用網(wǎng)絡(luò)帶寬,而且非CPU密集型的動(dòng)作是有用的(CPU密集型動(dòng)作是指,需要CPU高負(fù)荷運(yùn)轉(zhuǎn),占用較多內(nèi)存,執(zhí)行大量處理才能完成的動(dòng)作)。??異步控制器解決的問題應(yīng)當(dāng)是,線程池與所處理的請(qǐng)求類型之間搭配不當(dāng)?shù)臓顟B(tài)。線程池意在確保每個(gè)請(qǐng)求到得到一片服務(wù)器資源,但是很可能停滯于一組無所事事的工作線程上。如果對(duì)CPU密集型動(dòng)作使用額外的后臺(tái)線程,那么會(huì)因?yàn)樯婕疤嗟牟l(fā)請(qǐng)求而削弱服務(wù)器資源。
創(chuàng)建一個(gè)RemoteData常規(guī)同步控制器,如下圖所示:?
public class RemoteDataController : Controller{// GET: RemoteDatapublic ActionResult Index(){return View();}public ActionResult Data(){RemoteService service = new RemoteService();string data = service.GetRemoteData();return View((object)data);}}? ?RemoteService 實(shí)例代碼如下圖所示:
public class RemoteService{public string GetRemoteData(){Thread.Sleep(2000);return "Hello from the other side of world";}}添加對(duì)動(dòng)作Data的新視圖,如下圖所示?:
@model string @{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>Data</title> </head> <body><div> Data:@Model</div> </body> </html>運(yùn)行效果如下圖所示:?
? ? ? ? ? ? ??
創(chuàng)建異步控制器?
使用關(guān)鍵字await 和 async,創(chuàng)建一個(gè)新的Task對(duì)象,并await它的響應(yīng)。修改Data動(dòng)作器代碼如下圖所示:
public async Task<ActionResult> Data() {RemoteService service = new RemoteService();string data = await Task<string>.Factory.StartNew( () => { return new RemoteService().GetRemoteData(); });return View((object)data); }在控制器中使用異步方法?
也可以在其他地方通過異步控制器來使用異步方法,在RemoteService.cs中添加如下方法:
public class RemoteService {public string GetRemoteData(){Thread.Sleep(2000);return "Hello from the other side of world";}public async Task<string> GetRemoteDataAsync(){return await Task<string>.Factory.StartNew(() => { Thread.Sleep(2000); return "Hello from the other side of the world"; });} }在控制器中調(diào)用異步方法:?
public class RemoteDataController : Controller {// GET: RemoteDatapublic ActionResult Index(){return View();}public async Task<ActionResult> Data(){RemoteService service = new RemoteService();string data = await Task<string>.Factory.StartNew( () => { return new RemoteService().GetRemoteData(); });return View((object)data);}public async Task<ActionResult> ConsumeAsyncMethod(){string data = await new RemoteService().GetRemoteDataAsync();return View("Data", (object)data);} }?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的精通ASP.NET MVC ——控制器可扩展性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蒙特卡洛法求圆周率 c语言,c++蒙特卡
- 下一篇: mysql5.7空间运算,深度解析MyS