接着上篇文章asp.net mvc源码分析-Action篇 Action的执行 ,现在Action已经执行并且返回结果,在ControllerActionInvoker.InvokeAction方法中 ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);这句已经执行完毕,现在看看InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);这句是怎么搞得,InvokeActionResultWithFilters方法的代码结构InvokeActionMethodWithFilters一致,从这里我们知道真正的结果处理调用时 InvokeActionResult(controllerContext, actionResult);是这句。该方法的其他部分是调用filter的。InvokeActionResult这个方法很简单就一句
actionResult.ExecuteResult(controllerContext);
我们发现RedirectToRouteResult,RedirectResult,JsonResult,JavaScriptResult,HttpStatusCodeResult,FileResult,ContentResult这几个东西都没有涉及到我们的view,直接继承于ActionResult,他们的特点是直接输出内容,我们以ContentResult为例,它的主要代码如下:
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType)) {
response.ContentType = ContentType;
}
if (ContentEncoding != null) {
response.ContentEncoding = ContentEncoding;
}
if (Content != null) {
response.Write(Content);
}
}
}
同样JavaScriptResult的ExecuteResult也是类似
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "application/x-javascript";
if (Script != null) {
response.Write(Script);
}
}
现在 我们把重心放到涉及到view的ViewResultBase,而ViewResult集成于ViewResultBase且重写了FindView方法。
ViewResultBase有几个属性
public TempDataDictionary TempData
public dynamic ViewBag
public ViewDataDictionary ViewData
public object Model
public string ViewName
public IView View
其中 TempData、ViewBag、ViewData的作用和Controll而中对应的属性一致,而Model=ViewData.Model这个也没什么说的了,ViewName简单不说了,通过ViewName可以构建一个View,总之这几个属性很好理解。这里还有一个比较特殊的属性ViewEngineCollection
public ViewEngineCollection ViewEngineCollection {
get {
return _viewEngineCollection ?? ViewEngines.Engines;
}
set {
_viewEngineCollection = value;
}
}
public static class ViewEngines {
private readonly static ViewEngineCollection _engines = new ViewEngineCollection {
new WebFormViewEngine(),
new RazorViewEngine(),
};
public static ViewEngineCollection Engines {
get {
return _engines;
}
}
}
mvc默认给我们提供了WebFormViewEngine和RazorViewEngine,他们分别对应着WebFormView和RazorView这个两个IView的实现,他们都继承于BuildManagerCompiledView。从这个Engines 的定义我们知道我们可以采用自己的ViewEngine,只需要我们在Application_Start方法中添加一句ViewEngines.Engines.Add(xxx);就可以实现自己的ViewEngine。
我们还是先看看Action的返回view()都做了什么。
protected internal ViewResult View() {
return View(null /* viewName */, null /* masterName */, null /* model */);
}
protected internal ViewResult View(object model) {
return View(null /* viewName */, null /* masterName */, model);
}
protected internal ViewResult View(string viewName) {
return View(viewName, null /* masterName */, null /* model */);
}
protected internal ViewResult View(string viewName, string masterName) {
return View(viewName, masterName, null /* model */);
}
protected internal ViewResult View(string viewName, object model) {
return View(viewName, null /* masterName */, model);
}
protected internal virtual ViewResult View(string viewName, string masterName, object model) {
if (model != null) {
ViewData.Model = model;
}
return new ViewResult {
ViewName = viewName,
MasterName = masterName,
ViewData = ViewData,
TempData = TempData
};
}
protected internal ViewResult View(IView view) {
return View(view, null /* model */);
}
protected internal virtual ViewResult View(IView view, object model) {
if (model != null) {
ViewData.Model = model;
}
return new ViewResult {
View = view,
ViewData = ViewData,
TempData = TempData
};
}
从 这里我们可以知道ViewResult的ViewData 就是Controller的ViewData ,TempData 也是Controller的TempData ,ViewResult要么指定View要么指定ViewName。和MasterName。
ViewResult的MasterName如下:
public string MasterName {
get {
return _masterName ?? String.Empty;
}
set {
_masterName = value;
}
}
默认 为空,而在Action返回函数调用最多的还是指定ViewName的这种情况要多一些。
现在 来看看你ViewResultBase中的ExecuteResult方法,
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new 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);
}
}
从这段代码我们知道,如果 ViewName为空我们把它设为Action的name,如果view为空,我们就调用FindView方法查找View,FindView方法主要是一句
ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
这里 具体是怎么查找view的我们就先不管了。
WebFormView和RazorView都继承于BuildManagerCompiledView。
TextWriter writer = context.HttpContext.Response.Output;这句就说明说的了,
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);这句就是实例化一个ViewContext ,没什么特别的只是注意一下ViewContext 继承于ControllerContext。
View.Render(viewContext, writer);这句其实调用的是BuildManagerCompiledView的Render方法:
public void Render(ViewContext viewContext, TextWriter writer) {
if (viewContext == null) {
throw new ArgumentNullException("viewContext");
}
object instance = null;
Type type = BuildManager.GetCompiledType(ViewPath);
if (type != null) {
instance = _viewPageActivator.Create(_controllerContext, type);
}
if (instance == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.CshtmlView_ViewCouldNotBeCreated,
ViewPath
)
);
}
RenderView(viewContext, writer, instance);
}
Type type = BuildManager.GetCompiledType(ViewPath);这句很好理解根据当前的view的路径找到相应的Type。实际上就是调用是这句 BuildManager.GetCompiledType(virtualPath),System.Web.dll中BuildManager的具体实现很复杂我们就不管了。这里我们需要注意的是,
mvc中的view默认是在第一次运行时动态编译的,并且不同路径下的view是编译到不同的dll中,编译出来的一个WebViewPageinstance = _viewPageActivator.Create(_controllerContext, type);这句就是创建一个view类型实例。从BuildManagerCompiledView的构造函数我们知道viewPageActivator 默认是一个DefaultViewPageActivator,
_viewPageActivator = viewPageActivator ?? new BuildManagerViewEngine.DefaultViewPageActivator(dependencyResolver);
DefaultViewPageActivator的Create方法实现如下:
return _resolverThunk().GetService(type) ?? Activator.CreateInstance(type);这种代码在讲Controller是就讲到了,这里不再重复了。
最后在调用 RenderView(viewContext, writer, instance);,这个方法在各自的子类实现了的。
RenderView方法的实现也是很复杂了,先放到后面再说。
现在让我们又回到ExecuteResult方法中来,还有最后一句 if (result != null) {
result.ViewEngine.ReleaseView(context, View);
}
WebFormViewEngine、RazorViewEngine都继承于BuildManagerViewEngine,BuildManagerViewEngine又继承于VirtualPathProviderViewEngine,ReleaseView的实现在VirtualPathProviderViewEngine中实现的,代码如下:
public virtual void ReleaseView(ControllerContext controllerContext, IView view) {
IDisposable disposable = view as IDisposable;
if (disposable != null) {
disposable.Dispose();
}
}
就是调用view的Dispose方法。而BuildManagerCompiledView没有实现IDisposable接口,所以实际上是没有调用所谓的Dispose方法。