您的位置:首页 > 编程语言 > ASP

asp.net mvc源码分析-ActionResult篇 ViewResult

2012-11-11 11:04 681 查看
接着上篇文章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中,编译出来的一个WebViewPage<TModel>子类。

instance = _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方法。

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: