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

接着上篇文章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方法

  

posted on 2012-11-11 11:04  dz45693  阅读(2970)  评论(0编辑  收藏  举报

导航