上一篇了解了请求至Controller的Action过程,这篇继续看源码处理Action收到
请求数据再返回ActionResult到View的过程。
本节要讨论的问题
- Action的传参过程
- ActionResult
- IView / IViewEngine / ViewEngineCollection / ViewEngineResult
记得上篇反编译源看到Filter的执行顺序提到命名1,2,3的变量,在MVC3的源码中这个微软改掉了。
AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
一、Action的传参过程
我们在定义Action方法时常用的传参大致有这么几种,详细看代码
注释
class="code_img_closed" src="/Upload/Images/2013072818/0015B68B3C38AA5B.gif" alt="" />
logs_code_hide('d8b1315b-b924-4cfd-921b-fe7e941f3ae8',event)" src="/Upload/Images/2013072818/2B1B950FA3DF188F.gif" alt="" />
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;
using Demo.Model;
using Demo.Service.IService;
namespace Demo.Mvc.Controller
{
[HandleError]
publicclass MenuController:Core.BaseController
{
private IMenuService service;
public MenuController(IMenuService service)
{
this.service = service;
}
public ActionResult Index()
{
return View();
}
[ActionName("ShowId")]
public ActionResult ShowId(string id)
{
return View(service.Get(id));
}
//UpdateModel 缺点失败会抛出异常 or TryUpdateModel方式会试转换时间整数等NULL字段
public ActionResult SaveModel(string name, string age)
{
var permisson =new INFRA_MENU_PERMISSION();
//UpdateModel<INFRA_MENU_PERMISSION>(permisson);
TryUpdateModel(permisson);
ViewData.Model = permisson;
return View();
}
//FormCollection 的方式
//可以绑定任意集合类型:IList<Book>, ICollection<Book>, IEnumerable<Book>, List<Book>, Book[] 字典
public ActionResult SaveModel(FormCollection formCollection)
{
INFRA_MENU_PERMISSION permisson =new INFRA_MENU_PERMISSION();
permisson.ACTION_NAME = formCollection["ACTION_NAME"];
permisson.CREATETIME = Convert.ToDateTime(formCollection["ACTION_NAME"]);
service.Update(permisson);
return RedirectToAction("Index");
}
//IModelBinder DefaultModelBinder将Request传递参数绑定到对象上
[OutputCache]
public ActionResult SaveModel(INFRA_MENU_PERMISSION permisson)
{
service.Update(permisson);
return RedirectToAction("Index");
}
//不绑定哪些属性
public ActionResult SaveModelWithExclude([Bind(Exclude ="CONTROLLER_NAME")]INFRA_MENU_PERMISSION permisson)
{
service.Update(permisson);
return RedirectToAction("Index");
}
//绑定哪些属性
public ActionResult SaveModelWithInclude([Bind(Include ="CONTROLLER_NAME")]INFRA_MENU_PERMISSION permisson)
{
service.Update(permisson);
return RedirectToAction("Index");
}
}
}
Action传参支持类型绑定,字典键,对象,集合等等。除了使用默认的绑定方式,还可以继承IModelBinder重写DefaultModelBinder。
自己实现一个ModelBinder的代码 以及处理绑定的Attribute (具体看代码注释),可以通过这种方式解决一些
复杂对象的值绑定问题。
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;
namespace Demo.Mvc.ExModelBinder
{
//实现类似DefaultModelBinder 参照一下DefaultModelBinder源码重写个ModelBinder
//DefaultModelBinder 在MVC2和MVC3的源码是不同的,主要是ValueProvider这个字段 MVC2是字典个集合,MVC3是个策略模式
publicclass VOModelBinder : IModelBinder
{
#region IModelBinder 成员
publicobject BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object model = Activator.CreateInstance(bindingContext.ModelType);
PropertyDescriptorCollection col = TypeDescriptor.GetProperties(model);
foreach (PropertyDescriptor item in col)
{
//ValueProvider记录Request参数键值对
var result = bindingContext.ValueProvider.GetValue(item.Name);
if (result !=null)
{
var value = result.ConvertTo(item.PropertyType);
item.SetValue(model, value);
}
}
return model;
//object model = Activator.CreateInstance(bindingContext.ModelType);
//var properties = bindingContext.ModelType.GetProperties();
//foreach (var item in properties)
//{
// if (bindingContext.PropertyFilter(item.Name))
// {
// var result = bindingContext.ValueProvider.GetValue(item.Name);
// var value = result.ConvertTo(item.PropertyType);
// item.SetValue(model, value, null);
// }
//}
//return model;
}
#endregion
}
///<summary>
/// 选择ModelBinder的标记属性 (如果不通过属性标记,就要在Global全局文件中注册这个绑定)
///</summary>
publicclass VOModelBinderAttribute : CustomModelBinderAttribute
{
publicoverride IModelBinder GetBinder()
{
returnnew VOModelBinder();
}
}
}
使用
自定义的ModelBinder处理参数转换对象有
三种方式
1.Global全配置
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web;
namespace Demo.Mvc
{
publicclass DemoApplication : HttpApplication
{
protectedvirtualvoid OnStart()
{
InitializeContainer();
RegisterRoutes(RouteTable.Routes);
}
privatevoid InitializeContainer()
{
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(ContainerFactory.GetContainer()));
}
publicstaticvoid RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller ="Menu", action ="Index", id ="" } // Parameter defaults
);
}
protectedvoid Application_Start()
{
OnStart();
//通过全局配置解决处理某个对象绑定使用的VOModelBinder
ModelBinders.Binders.Add(typeof(Demo.Model.INFRA_MENU_PERMISSION), new ExModelBinder.VOModelBinder());
}
}
}
2.利用我们自定义的属性标记
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Demo.Mvc.ExModelBinder;
namespace Demo.Mvc.ViewObject
{
//[ModelBinder(typeof(VOModelBinder))] //使用ModelBinderAttribute标记
[VOModelBinder] //使用自定义VOModelBinderAttribute标记(属性类已指明使用VOModelBinder来处理数据绑定)
publicclass UserVO
{
publicstring Name { set; get; }
publicbool Sex { set; get; }
publicstring Address { set; get; }
public DateTime Birthday { set; get; }
}
}
3.Action参数标记
View Code
public ActionResult Index([ExModelBinder.VOModelBinder]UserVO userVo)
{
return View();
}
以上就是Action参数和Request参数之间转换过程,使用默认的DefaultModelBinder会根据参数的复杂情况采用不同的方式处理参数。
View Code
publicvirtualobject BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
if (bindingContext ==null) {
thrownew ArgumentNullException("bindingContext");
}
bool performedFallback =false;
if (!String.IsNullOrEmpty(bindingContext.ModelName) &&!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) {
// We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
// to the empty prefix.
if (bindingContext.FallbackToEmptyPrefix) {
bindingContext =new ModelBindingContext() {
ModelMetadata = bindingContext.ModelMetadata,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
performedFallback =true;
}
else {
returnnull;
}
}
// Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
// or by seeing if a value in the request exactly matches the name of the model we're binding.
// Complex type = everything else.
if (!performedFallback) {
bool performRequestValidation = ShouldPerformRequestValidation(controllerContext, bindingContext);
ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
if (vpResult !=null) {
return BindSimpleModel(controllerContext, bindingContext, vpResult); //简单参数处理----------
}
}
if (!bindingContext.ModelMetadata.IsComplexType) {
returnnull;
}
return BindComplexModel(controllerContext, bindingContext); //复杂对象的处理--------------------------
}
而我们自定义处理参数的规则就由自己来写了。 了解这些参数转换问题,那这个过程是何时发生的?根据前篇文章的分析,这个过程肯定是发生在Controller 的策略ControllerActionInvoker在执行InvokeAction方法拦截Action的时候发生的,即在处理完IAuthorizationFilter过滤器之后,处理IActionFilter之前的这段代码
IDictionary<string, object> parameters = GetParameter
Values(controllerContext, actionDescriptor);
不管使用哪种ModelBinder,通过GetParameterValues将Request 请求的参数转换对应的类型。看一下跟进的方法是如
何处理的
View Code
//获取参数信息的描述属性并遍历处理
protectedvirtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
Dictionary<string, object> parametersDict =new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters();
foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) {
parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
}
return parametersDict;
}
//单独处理每种参数描术属性
protectedvirtualobject GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {
// collect all of the necessary binding properties
Type parameterType = parameterDescriptor.ParameterType;
IModelBinder binder = GetModelBinder(parameterDescriptor);
IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);
// finally, call into the binder
ModelBindingContext bindingContext =new ModelBindingContext() {
FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix ==null), // only fall back if prefix not specified
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
ModelName = parameterName,
ModelState = controllerContext.Controller.ViewData.ModelState,
PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
object result = binder.BindModel(controllerContext, bindingContext);
return result ?? parameterDescriptor.DefaultValue;
}
主要业务都在第二个处理参数描述的取值的方法里,首先取得参数的类型,然后读取对应的ModelBinder,如果ModelBinder为空的时候则使用默认的DefaultModelBinder,接着生成ModelBindingContext上下文,然后又触发ModelBinder的BindModel方法,并将ModelBindingContext传递过去,
在BindModel中默认的或者自定义实现的对象属性赋值过程了,最后将生成的parameters对象集合再传回至要触发的Action方法中。
二、ActionResult
ActionResult是Action的返回结果。ActionResult 有多个派生类,每个子类功能均不同,并不是所有的子类都需要返回视图View,有些直接返回流,有些返回字符串等。我们来看一下ActionResult派生类关系图
具体看一下每个类的功能,由于
MSDN的示意图太简单不能完全表现所有的子类功能
类名
抽象类
父类
功能
ActionResult
abstract
Object
顶层父类
ContentResult
根据内容的类型和编码,数据内容.通过Controller的Content方法返回
EmptyResult
返回空结果
FileResult
abstract
写入文件内容,具体的写入方式在派生类中.
FileContentResult
FileResult
通过 文件byte[] 写入Response 返回客户端,Controller的File方法
FilePathResult
FileResult
通过 文件路径 写入Response 返回客户端,Controller的File方法
FileStreamResult
FileResult
通过 Stream 写入Response 返回客户端,Controller的File方法
HttpUnauthorizedResult
抛出401错误
JavaScriptResult
返回javascript文件
JsonResult
返回Json格式的数据
RedirectResult
使用Response.Redirect重定向页面
RedirectToRouteResult
根据Route规则重定向页面
ViewResultBase
abstract
调用IView.Render() 返回视图,两个常用属性ViewData,TempData
PartialViewResult
ViewResultBase
调用父类ViewResultBase 的ExecuteResult方法. 重写了父类的FindView方法. 寻找用户控件.ascx文件
ViewResult
ViewResultBase
调用父类ViewResultBase 的ExecuteResult方法.
重写了父类的FindView方法.
寻找视图页面(aspx,cshtml或自定义视图)
Controller的View()方法默认封装ViewResult返回结果
简单的列几种写法,都是Controller已经封装好的
View Code
public ActionResult ShowContent()
{
return Content("测试ContentResult方法"); //默认封装ContentResult文本返回
}
public ActionResult Index(UserVO userVo)
{
return View(); //默认封装ViewResult返回
}
public ActionResult DownLoadFile(string fileName)
{
return File(Server.MapPath(@"/Images/view.jpg"), @"image/gif");
}
public ActionResult ToOther(string fileName)
{
return Redirect(@"http://localhost:1847/Menu/ShowContent");
}
当然你可以自己实现每种的类型返回,而不是通过Controller的方法返回。这个环节最重要的问题,当Action返回ActionResult后,这个ActionResult是如何工作的?ActionResult只有一个抽象方法 ExecuteResult ,当ActionResult实例被返回后,Controller执行器ControllerActionInvoker的InvokeAction方法在处理完IActionFilter之后调用了这段代码InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
看后两个参数,一个是IResultFilter过滤器,一个是Action返回的Result。该方法对返回ActionResult进行前置拦截后,接着调用ActionResult的ExecuteResult方法去处对应的响应业务(返回视图,或字符串,文件流等),最后又对ActionResult后置拦截了一次。调用棧比较深
View Code
//1---------------------------------
protectedvirtual ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext, IList<IResultFilter> filters, ActionResult actionResult) {
ResultExecutingContext preContext =new ResultExecutingContext(controllerContext, actionResult);
//InvokeActionResult 做为委托被前置与后置包围了
Func<ResultExecutedContext> continuation =delegate {
InvokeActionResult(controllerContext, actionResult);
returnnew ResultExecutedContext(controllerContext, actionResult, false/* canceled */, null/* exception */);
};
// need to reverse the filter list because the continuations are built up backward
Func<ResultExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionResultFilter(filter, preContext, next));
return thunk();
}
//2--------------------------------------------
internalstatic ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
filter.OnResultExecuting(preContext); //前置拦截----------------------------------------
if (preContext.Cancel) {
returnnew ResultExecutedContext(preContext, preContext.Result, true/* canceled */, null/* exception */);
}
bool wasError =false;
ResultExecutedContext postContext =null;
try {
postContext = continuation(); //ActionResult的ExecuteResult的调用环节------------------------------------------
}
catch (ThreadAbortException) {
// This type of exception occurs as a result of Response.Redirect(), but we special-case so that
// the filters don't see this as an error.
postContext =new ResultExecutedContext(preContext, preContext.Result, false/* canceled */, null/* exception */);
filter.OnResultExecuted(postContext); //出错了,后置拦截----------------------------------
throw;
}
catch (Exception ex) {
wasError =true;
postContext =new ResultExecutedContext(preContext, preContext.Result, false/* canceled */, ex);
filter.OnResultExecuted(postContext);
if (!postContext.ExceptionHandled) {
throw;
}
}
if (!wasError) {
filter.OnResultExecuted(postContext); //后置拦截----------------------------------------------
}
return postContext;
}
//3------------------------------------------------------------------
protectedvirtualvoid InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {
actionResult.ExecuteResult(controllerContext);
}
注释的地方注意看一下,InvokeActionResult 是调用ActionResult.ExecuteResult的方法,被做为委托放到IResultFilter前后拦截法中间执行。Controller的执行器ControllerActionInvoker 这几个环节的调度者。再看一下ActionResult.ExecuteResult方法的业务,挑选个有代表性的子类实现的业务贴上来,
FileResult基类 的ExecuteResult,但又调用了WriteFile方法,这个方法又下放到子类中实现了
View Code
publicoverridevoid ExecuteResult(ControllerContext context) {
if (context ==null) {
thrownew ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = ContentType;
if (!String.IsNullOrEmpty(FileDownloadName)) {
// From RFC 2183, Sec. 2.3:
// The sender may want to suggest a filename to be used if the entity is
// detached and stored in a separate file. If the receiving MUA writes
// the entity to a file, the suggested filename should be used as a
// basis for the actual filename, where possible.
string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
}
WriteFile(response);
}
我们挑选FileStreamResult子类的WriteFile方法看看
View Code
protectedoverridevoid WriteFile(HttpResponseBase response) {
// grab chunks of data and write to the output stream
Stream outputStream = response.OutputStream;
using (FileStream) {
byte[] buffer =newbyte[_bufferSize];
while (true) {
int bytesRead = FileStream.Read(buffer, 0, _bufferSize);
if (bytesRead ==0) {
// no more data
break;
}
outputStream.Write(buffer, 0, bytesRead);
}
}
}
整个过程看下来FileStreamResult的ExecuteResult 将文件流写入HttpResponse中返回到客户端,而并不是返回视图。再看一下ViewResult的ExecuteResult的业务,这个业务是在父类的中实现的
ViewResultBase的ExecuteResult业务,ViewResultBase还有两个重要的性ViewData,TempData是在Acion返回的时候封装好的。
View Code
publicoverridevoid ExecuteResult(ControllerContext context) {
if (context ==null) {
thrownew ArgumentNullException("context");
}
if (String.IsNullOrEmpty(ViewName)) {
ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result =null;
if (View ==null) {
result = FindView(context);
View = result.View;
}
TextWriter writer = context.HttpContext.Response.Output;
ViewContext viewContext =new ViewContext(context, View, ViewData, TempData, writer);
View.Render(viewContext, writer);
if (result !=null) {
result.ViewEngine.ReleaseView(context, View);
}
}
先根据上下文中的路由+Action名找到对应的IView,然后调用IView的Render会出视图写入context.HttpContext.Response.Output返回到客户端。
三、IView / IViewEngine / ViewEngineCollection / ViewEngineResult
- IView 作用:展示View对象, 将页面读成流通过Writer写入Response中返回客户端,主要方法Render用来绘制DOM对象到流中。
- IViewEngine 作用:查找View对象(视图页面,例如aspx页面),但是返回结果是ViewEngineResult ,View被保存其中。主要方法FindView,FindPartialView。
- ViewEngineCollection 作用:视图引擎集合
- ViewEngineResult 作用:是IViewEngine查找View的结果
实现过程:当ViewResult执行ExecuteResult时会遍历ViewEngineCollection中所有IViewEngine 引擎,并调用每个IViewEngine 引擎的FindView,如果找到具体的View页面,则返回ViewEngineResult,ViewEngineResult包含了相关的IView信息,最后再调用IView的Render输出视图。
ViewEngineCollection 是视图引擎集合(当IView要Render视图的时候,要从视图引擎集合中选择一个视图引擎来实现),ViewEngineCollection是ViewResult的策略属性。可以通过属性注入的方式更换成其它视图引擎包括自定义的视图引擎。MVC3中ViewResult的属性ViewEngineCollection被默认注入了ViewEngines.Engines。
View Code
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification ="This entire type is meant to be mutable.")]
public ViewEngineCollection ViewEngineCollection {
get {
return _viewEngineCollection ?? ViewEngines.Engines;
}
set {
_viewEngineCollection = value;
}
}
再看一下ViewEngines.Engines类默认两个成员,一个处理aspx的WebFormViewEngine引擎,一个是MVC3新加入的RazorViewEngine引擎。
View Code
publicstaticclass ViewEngines {
privatereadonlystatic ViewEngineCollection _engines =new ViewEngineCollection {
new WebFormViewEngine(),
new RazorViewEngine(),
};
publicstatic ViewEngineCollection Engines {
get {
return _engines;
}
}
}
我们看一WebFormViewEngine类
View Code
publicclass WebFormViewEngine : BuildManagerViewEngine {
public WebFormViewEngine()
: this(null) {
}
public WebFormViewEngine(IViewPageActivator viewPageActivator)
:base(viewPageActivator){
MasterLocationFormats =new[] {
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};
AreaMasterLocationFormats =new[] {
"~/Areas/{2}/Views/{1}/{0}.master",
"~/Areas/{2}/Views/Shared/{0}.master",
};
ViewLocationFormats =new[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
AreaViewLocationFormats =new[] {
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = AreaViewLocationFormats;
FileExtensions =new[] {
"aspx",
"ascx",
"master",
};
}
protectedoverride IView CreatePartialView(ControllerContext controllerContext, string partialPath) {
returnnew WebFormView(controllerContext, partialPath, null, ViewPageActivator);
}
protectedoverride IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
returnnew WebFormView(controllerContext, viewPath, masterPath, ViewPageActivator);
}
}
里面已经指定了要查找View页的路径,主要是针对模板master,aspx,ascx等页面相对路径。再看他的爷爷类的FindView方法
View Code
publicvirtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
if (controllerContext ==null) {
thrownew ArgumentNullException("controllerContext");
}
if (String.IsNullOrEmpty(viewName)) {
thrownew ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
}
string[] viewLocationsSearched;
string[] masterLocationsSearched;
string controllerName = controllerContext.RouteData.GetRequiredString("controller");
string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);
string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched);
if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) &&!String.IsNullOrEmpty(masterName))) {
returnnew ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
}
returnnew ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
}
privatestring GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, outstring[] searchedLocations) {
searchedLocations = _emptyLocations;
if (String.IsNullOrEmpty(name)) {
return String.Empty;
}
string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
bool usingAreas =!String.IsNullOrEmpty(areaName);
List<ViewLocation> viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null);
if (viewLocations.Count ==0) {
thrownew InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
MvcResources.Common_PropertyCannotBeNullOrEmpty, locationsPropertyName));
}
bool nameRepresentsPath = IsSpecificPath(name);
string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName);
if (useCache) {
return ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
}
return (nameRepresentsPath) ?
GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) :
GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);
}
根据ControolerName+ActionName替换掉路径中{0}去查找对应的物理文件。第一次查找到了会保存到
缓存中,
第二次进来的时候直接从缓存中查找。
从Request->Controller->Action->ActionResult->View->Response过程到此就介绍完了。
突然想到一个问题,这个位置微软都使用了缓存,为什么每次DefaultControllerFactory都要直接反射Controller呢,而不一次反射就缓存下来提高效率,反而是让开发人员利用IOC容器去解决这个问题。 带这个疑问去看了MVC3的DefaultControllerFactory,这个问题已经解决了。DefaultControllerFactory新增了一个DependencyResolver来处理获取Controller的工作。但这个功能还是没有IOC容器的功能强大,因为IOC解决了构造注入的问题,而DependencyResolver则没有实现这个功能。.net mvc 已经整合了微软企业库很多内容,如果再把IOC功能直接整合进来,似乎企业库有点
尴尬了。好了,这节就到这里了。相信你对.net MVC实现原理应该有了一定的了解。
http://www.cnblogs.com/mecity/archive/2011/06/29/2092042.html