zoukankan      html  css  js  c++  java
  • 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方法

      

  • 相关阅读:
    认识“委托”
    程序员的修炼之道:从小工到专家(一)
    知识的使用 与 知识的内化
    VB.Net中 Module 的前世今生
    memcached
    C#知识
    Android之垂直显示TextView
    Android开发之各个语言
    Android之hint提示字体大小修改,显示完全
    Android 之计算控件颜色透明度
  • 原文地址:https://www.cnblogs.com/majiang/p/2764903.html
Copyright © 2011-2022 走看看