Asp.Net MVC<八>:View的呈现
1、 ActionResult
原则上任何类型的响应都可以利用当前的HttpResponse来完成。但是MVC中我们一般将针对请求的响应实现在一个ActionResult对象中。
1 2 3 4 5 6 | public abstract class ActionResult { protected ActionResult(); public abstract void ExecuteResult(ControllerContext context); } |
2、 ViewResult和ViewEngine
2.1、 IViewEngine
viewResult通过ViewEngine实现对View的获取、激活和呈现。
1 2 3 4 5 6 7 8 | public interface IViewEngine { ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache); ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache); void ReleaseView(ControllerContext controllerContext, IView view); } |
现有两种实现:WebFormViewEngine(.aspx,.ascx),RazorViewEngine(.cshtml/vbhtml)
1 2 3 4 5 6 7 8 | public void Index() { ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, "NonExistView" , null ); foreach ( var item in result.SearchedLocations) { Response.Write(item + "<br/>" ); } } |
因为WebFormViewEngine排在RazorViewEngine之前,所以前者被优先使用。可以用 ViewEngines.Engines.RemoveAt(0);来移除对WebFormViewEngine的调用。
2.2、 ViewResult
1 2 3 4 5 6 | public class ViewResult : ViewResultBase { public string MasterName { get ; set ; } protected override ViewEngineResult FindView(ControllerContext context); } |
2.3、 ViewResultBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | public abstract class ViewResultBase: ActionResult { public object Model { get ; } // 临时数据。 public TempDataDictionary TempData { get ; set ; } // 视图。 public IView View { get ; set ; } // 视图包 public dynamic ViewBag { get ; } // 视图数据。 public ViewDataDictionary ViewData { get ; set ; } // 视图引擎的集合。 public ViewEngineCollection ViewEngineCollection { get ; set ; } // 视图的名称。 public string ViewName { get ; set ; } // 视图引擎。 protected abstract ViewEngineResult FindView(ControllerContext context); public override void ExecuteResult(ControllerContext context) { if ( string .IsNullOrEmpty( this .ViewName)) { this .ViewName = context.RouteData.GetRequiredString( "action" ); } ViewEngineResult result = null ; if ( this .View == null ) { result = this .FindView(context); this .View = result.View; } TextWriter output = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext(context, this .View, this .ViewData, this .TempData, output); this .View.Render(viewContext, output); if (result != null ) { result.ViewEngine.ReleaseView(context, this .View); } } } |
抽象类Controller中的几个重载方法
3、 View的编译原理
Asp.net mvc默认情况下采用动态编译的方式对View文件实施编译。当我们在部署的时候,需要对.cshtml或.vbhtml文件进行打包。
针对某个文件的第一次访问会触发针对它的编译,一个View会被编译成一个具体的类型。View文件的每一次修改都会导致再一次编译。和.aspx页面一样的编译方式,默认是以目录为单位的,也就是同一个目录下的多个View被编译到同一个程序集中。
3.1、 View程序集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static class HtmlHelperExtensions { public static MvcHtmlString ListViewAssemblies( this HtmlHelper helper) { TagBuilder ul = new TagBuilder( "ul" ); foreach ( var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith( "App_Web_" ))) { TagBuilder li = new TagBuilder( "li" ); li.InnerHtml = assembly.FullName; ul.InnerHtml += li.ToString(); } return new MvcHtmlString(ul.ToString()); } } |
View:
1 2 3 | < div >当前View类型:@this.GetType().AssemblyQualifiedName</ div > < div >当前加载的View程序集:</ div > @Html.ListViewAssemblies() |
页面展示:
3.2、 BuildManager
Response.Write(BuildManager.GetCompiledType("~/Views/Bar/Action1.cshtml") + "<br/>");
Response.Write(BuildManager.GetCompiledType("~/Views/Bar/Action2.cshtml") + "<br/>");
Response.Write(BuildManager.TargetFramework.FullName+ "<br/>");
Response.Write(BuildManager.GetCompiledCustomString("~/Views/Foo/Action1.cshtml") + "<br/>");
Response.Write(BuildManager.GetCompiledAssembly("~/Views/Foo/Action1.cshtml").Location + "<br/>");
页面展示:
3.3、 WebViewPage的继承树
3.3.1、 由.cshtml文件编译后产生的类文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | namespace ASP { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Web; using System.Web.Helpers; using System.Web.Security; using System.Web.UI; using System.Web.WebPages; using System.Web.Mvc; using System.Web.Mvc.Ajax; using System.Web.Mvc.Html; using System.Web.Optimization; using System.Web.Routing; using MvcApp; public class _Page_Views_Foo_Action1_cshtml : System.Web.Mvc.WebViewPage<dynamic> { protected ASP.global_asax ApplicationInstance { get { return ((ASP.global_asax)(Context.ApplicationInstance)); } } public override void Execute() { BeginContext( "~/Views/Foo/Action1.cshtml" , 0, 14, true ); WriteLiteral( "<div>当前View类型:" ); EndContext( "~/Views/Foo/Action1.cshtml" , 0, 14, true ); BeginContext( "~/Views/Foo/Action1.cshtml" , 15, 36, false ); Write( this .GetType().AssemblyQualifiedName); EndContext( "~/Views/Foo/Action1.cshtml" , 15, 36, false ); BeginContext( "~/Views/Foo/Action1.cshtml" , 51, 34, true ); WriteLiteral( "</div>\r\n<div>当前加载的View程序集:</div>\r\n" ); EndContext( "~/Views/Foo/Action1.cshtml" , 51, 34, true ); BeginContext( "~/Views/Foo/Action1.cshtml" , 86, 25, false ); Write(Html.ListViewAssemblies()); EndContext( "~/Views/Foo/Action1.cshtml" , 86, 25, false ); BeginContext( "~/Views/Foo/Action1.cshtml" , 111, 2, true ); WriteLiteral( "\r\n" ); EndContext( "~/Views/Foo/Action1.cshtml" , 111, 2, true ); } } } |
View编译后的生成的类型是WebViewPage<TModel>的子类,WebViewPage<TModel>是WebViewPage的子类,泛型TModel是View的Model类型。
3.3.2、 WebPageExecutingBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public abstract class WebPageExecutingBase { public virtual dynamic App { get ; } public virtual HttpApplicationStateBase AppState { get ; } public virtual HttpContextBase Context { get ; set ; } public virtual string VirtualPath { get ; set ; } public virtual IVirtualPathFactory VirtualPathFactory { get ; set ; } public abstract void Execute(); public virtual string Href( string path, params object [] pathParts); public virtual string NormalizePath( string path); protected internal virtual TextWriter GetOutputWriter(); protected internal virtual string NormalizeLayoutPagePath( string layoutPagePath); //动态内容 public abstract void Write( object value); public abstract void Write(HelperResult result); public static void WriteTo(TextWriter writer, object content); public static void WriteTo(TextWriter writer, HelperResult content); //静态内容 public abstract void WriteLiteral( object value); public static void WriteLiteralTo(TextWriter writer, object content); public virtual void WriteAttribute( string name, PositionTagged< string > prefix, PositionTagged< string > suffix, params AttributeValue[] values); public virtual void WriteAttributeTo(TextWriter writer, string name, PositionTagged< string > prefix, PositionTagged< string > suffix, params AttributeValue[] values); protected internal virtual void WriteAttributeTo( string pageVirtualPath, TextWriter writer, string name, PositionTagged< string > prefix, PositionTagged< string > suffix, params AttributeValue[] values); protected internal void BeginContext( int startPosition, int length, bool isLiteral); protected internal void BeginContext( string virtualPath, int startPosition, int length, bool isLiteral); protected internal void BeginContext(TextWriter writer, int startPosition, int length, bool isLiteral); protected internal void BeginContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral); protected internal void EndContext( int startPosition, int length, bool isLiteral); protected internal void EndContext( string virtualPath, int startPosition, int length, bool isLiteral); protected internal void EndContext(TextWriter writer, int startPosition, int length, bool isLiteral); protected internal void EndContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral); } |
3.3.3、 WebPageRenderingBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public abstract class WebPageRenderingBase : WebPageExecutingBase, ITemplateFile { public virtual Cache Cache { get ; } public string Culture { get ; set ; } public virtual bool IsAjax { get ; } public virtual bool IsPost { get ; } public abstract string Layout { get ; set ; } // An object that contains page data. public abstract dynamic Page { get ; } public abstract IDictionary< object , dynamic> PageData { get ; } public WebPageContext PageContext { get ; } public ProfileBase Profile { get ; } public virtual HttpRequestBase Request { get ; } public virtual HttpResponseBase Response { get ; } public virtual HttpServerUtilityBase Server { get ; } public virtual HttpSessionStateBase Session { get ; } public virtual TemplateFileInfo TemplateInfo { get ; } public string UICulture { get ; set ; } public virtual IList< string > UrlData { get ; } public virtual IPrincipal User { get ; internal set ; } protected internal IDisplayMode DisplayMode { get ; } public abstract void ExecutePageHierarchy(); public abstract HelperResult RenderPage( string path, params object [] data); } |
ExecutePageHierarchy负责整个页面内容的输出,
View自身内容通过重写Execute方法来输出。
3.3.4、 WebPageBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | public abstract class WebPageBase : WebPageRenderingBase { //其他成员…… // Gets or sets the path of a layout page. public override string Layout { get ; set ; } public TextWriter Output { get ; } // Returns the text writer instance that is used to render the page. protected internal override TextWriter GetOutputWriter(); // Gets the stack of System.IO.TextWriter objects for the current page context. public Stack<TextWriter> OutputStack { get ; } // Returns and removes the context from the top of the System.Web.WebPages.WebPageBase.OutputStack // instance. public void PopContext(); // Inserts the specified context at the top of the System.Web.WebPages.WebPageBase.OutputStack // instance. public void PushContext(WebPageContext pageContext, TextWriter writer); // Executes the code in a set of dependent web pages. public override void ExecutePageHierarchy(); // Executes the code in a set of dependent web pages by using the specified parameters. public void ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer); // Executes the code in a set of dependent web pages by using the specified context, // writer, and start page. public void ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage); // Returns a value that indicates whether the specified section is defined in the // page. public bool IsSectionDefined( string name); // Called by content pages to create named content sections. public void DefineSection( string name, SectionWriter action); // In layout pages, renders the portion of a content page that is not within a named // section. public HelperResult RenderBody(); // Renders the content of one page within another page. public override HelperResult RenderPage( string path, params object [] data); // In layout pages, renders the content of a named section. public HelperResult RenderSection( string name); // In layout pages, renders the content of a named section and specifies whether // the section is required. public HelperResult RenderSection( string name, bool required); // Writes the specified object as an HTML-encoded string. public override void Write( object value); // Writes the specified System.Web.WebPages.HelperResult object as an HTML-encoded // string. public override void Write(HelperResult result); // Writes the specified object without HTML-encoding it first. public override void WriteLiteral( object value); } |
WebPageBase 实现了抽象方法ExecutePageHierarchy,定义了额外的两个ExecutePageHierarchy方法重载,参数startPage表示定义在“_ViewStart.cshtml”文件的开始页面。
最初呈现的内容来源于3个部分:布局文件,开始页面和View自身的内容。完整页面内容的呈现通过调用ExecutePageHierarchy方法来完成。
WebPageBase 实现了抽象方法Write/WriteLiteral和抽象属性 Layout。
定义了在布局文件上输出Section的方法。
3.3.5、 WebViewPage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public abstract class WebViewPage : WebPageBase, IViewDataContainer, IViewStartPageChild { // The System.Web.Mvc.AjaxHelper object that is used to render HTML using Ajax. public AjaxHelper< object > Ajax { get ; set ; } // The System.Web.HttpContext object that is associated with the page. public override HttpContextBase Context { get ; set ; } // The System.Web.Mvc.HtmlHelper object that is used to render HTML elements. public HtmlHelper< object > Html { get ; set ; } public object Model { get ; } public TempDataDictionary TempData { get ; } public UrlHelper Url { get ; set ; } public dynamic ViewBag { get ; } public ViewContext ViewContext { get ; set ; } public ViewDataDictionary ViewData { get ; set ; } // Runs the page hierarchy for the ASP.NET Razor execution pipeline. public override void ExecutePageHierarchy(); // Initializes the System.Web.Mvc.AjaxHelper, System.Web.Mvc.HtmlHelper, // and System.Web.Mvc.UrlHelper classes. public virtual void InitHelpers(); // Sets the view context and view data for the page. protected override void ConfigurePage(WebPageBase parentPage); protected virtual void SetViewData(ViewDataDictionary viewData); } |
3.3.6、 WebViewPage<TModel>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public abstract class WebViewPage<TModel> : WebViewPage { public AjaxHelper<TModel> Ajax { get ; set ; } public HtmlHelper<TModel> Html { get ; set ; } public TModel Model { get ; } public ViewDataDictionary<TModel> ViewData { get ; set ; } public override void InitHelpers(); protected override void SetViewData(ViewDataDictionary viewData); } |
提供针对泛型参数TModel的属性Ajax, Html, Model, ViewData, 而初始化它们的InitHelpers和SetViewData方法也被重写
3.4、 View的呈现
3.4.1、 IView
对应View引擎来说,View通过IView来表示
1 2 3 4 | public interface IView { void Render(ViewContext viewContext, TextWriter writer); } |
View的呈现体现在对WebViewPage对象的激活上。
在Asp.net mvc 中Razor引擎下的View通过RazorView对象来表示,WebForm引擎的View则通过WebFormView对象表示,二者都继承自BuildManagerCompiledView。
3.4.2、 RazorView 实现
BuildManagerCompiledView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | public abstract class BuildManagerCompiledView : IView { internal IViewPageActivator ViewPageActivator; private IBuildManager _buildManager; private ControllerContext _controllerContext; internal IBuildManager BuildManager { get { if ( this ._buildManager == null ) { this ._buildManager = new BuildManagerWrapper(); } return this ._buildManager; } set { this ._buildManager = value; } } public string ViewPath { get ; protected set ; } protected BuildManagerCompiledView(ControllerContext controllerContext, string viewPath) : this (controllerContext, viewPath, null ) { } protected BuildManagerCompiledView(ControllerContext controllerContext, string viewPath, IViewPageActivator viewPageActivator) : this (controllerContext, viewPath, viewPageActivator, null ) { } internal BuildManagerCompiledView(ControllerContext controllerContext, string viewPath, IViewPageActivator viewPageActivator, IDependencyResolver dependencyResolver) { if (controllerContext == null ) { throw new ArgumentNullException( "controllerContext" ); } if ( string .IsNullOrEmpty(viewPath)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewPath" ); } this ._controllerContext = controllerContext; this .ViewPath = viewPath; this .ViewPageActivator = (viewPageActivator ?? new BuildManagerViewEngine.DefaultViewPageActivator(dependencyResolver)); } public virtual void Render(ViewContext viewContext, TextWriter writer) { if (viewContext == null ) { throw new ArgumentNullException( "viewContext" ); } object obj = null ; Type compiledType = this .BuildManager.GetCompiledType( this .ViewPath); if (compiledType != null ) { obj = this .ViewPageActivator.Create( this ._controllerContext, compiledType); } if (obj == null ) { throw new InvalidOperationException( string .Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_ViewCouldNotBeCreated, new object [] { this .ViewPath })); } this .RenderView(viewContext, writer, obj); } protected abstract void RenderView(ViewContext viewContext, TextWriter writer, object instance); } |
RazorView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /// <summary>Represents the class used to create views that have Razor syntax.</summary> public class RazorView : BuildManagerCompiledView { public string LayoutPath { get ; private set ; } public bool RunViewStartPages { get ; private set ; } internal StartPageLookupDelegate StartPageLookup { get ; set ; } internal IVirtualPathFactory VirtualPathFactory { get ; set ; } internal DisplayModeProvider DisplayModeProvider { get ; set ; } public IEnumerable< string > ViewStartFileExtensions { get ; private set ; } public RazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable< string > viewStartFileExtensions) : this (controllerContext, viewPath, layoutPath, runViewStartPages, viewStartFileExtensions, null ) { } /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.RazorView" /> class using the view page activator.</summary> /// <param name="controllerContext">The controller context.</param> /// <param name="viewPath">The view path.</param> /// <param name="layoutPath">The layout or master page.</param> /// <param name="runViewStartPages">A value that indicates whether view start files should be executed before the view.</param> /// <param name="viewStartFileExtensions">The set of extensions that will be used when looking up view start files.</param> /// <param name="viewPageActivator">The view page activator.</param> public RazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable< string > viewStartFileExtensions, IViewPageActivator viewPageActivator) : base (controllerContext, viewPath, viewPageActivator) { this .LayoutPath = (layoutPath ?? string .Empty); this .RunViewStartPages = runViewStartPages; this .StartPageLookup = new StartPageLookupDelegate(StartPage.GetStartPage); this .ViewStartFileExtensions = (viewStartFileExtensions ?? Enumerable.Empty< string >()); } /// <summary>Renders the specified view context by using the specified writer and <see cref="T:System.Web.Mvc.WebViewPage" /> instance.</summary> /// <param name="viewContext">The view context.</param> /// <param name="writer">The writer that is used to render the view to the response.</param> /// <param name="instance">The <see cref="T:System.Web.Mvc.WebViewPage" /> instance.</param> protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance) { if (writer == null ) { throw new ArgumentNullException( "writer" ); } WebViewPage webViewPage = instance as WebViewPage; if (webViewPage == null ) { throw new InvalidOperationException( string .Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_WrongViewBase, new object [] { base .ViewPath })); } webViewPage.OverridenLayoutPath = this .LayoutPath; webViewPage.set_VirtualPath( base .ViewPath); webViewPage.ViewContext = viewContext; webViewPage.ViewData = viewContext.ViewData; webViewPage.InitHelpers(); if ( this .VirtualPathFactory != null ) { webViewPage.set_VirtualPathFactory( this .VirtualPathFactory); } if ( this .DisplayModeProvider != null ) { webViewPage.set_DisplayModeProvider( this .DisplayModeProvider); } WebPageRenderingBase webPageRenderingBase = null ; if ( this .RunViewStartPages) { webPageRenderingBase = this .StartPageLookup(webViewPage, RazorViewEngine.ViewStartFileName, this .ViewStartFileExtensions); } webViewPage.ExecutePageHierarchy( new WebPageContext(viewContext.HttpContext, null , null ), writer, webPageRenderingBase); } } |
4、 以IoC的方式激活View
RazorView 的构造函数中有用到IViewPageActivator,IViewPageActivator旨在View对象的激活。当没有指定一个具体的ViewPageActivator时,默认采用 DefaultViewPageActivator对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | internal class DefaultViewPageActivator : IViewPageActivator { private Func<IDependencyResolver> _resolverThunk; public DefaultViewPageActivator() : this ( null ) { } public DefaultViewPageActivator(IDependencyResolver resolver) { if (resolver == null ) { this ._resolverThunk = (() => DependencyResolver.Current); return ; } this ._resolverThunk = (() => resolver); } public object Create(ControllerContext controllerContext, Type type) { object result; try { result = ( this ._resolverThunk().GetService(type) ?? Activator.CreateInstance(type)); } catch (MissingMethodException originalException) { MissingMethodException ex = TypeHelpers.EnsureDebuggableException(originalException, type.FullName); if (ex != null ) { throw ex; } throw ; } return result; } } |
5、 扩展
5.1、 扩展WebViewPage
IDependencyResolver 是Asp.net mvc提供的IoC接入口,所以这里可以介入到具体某个WebViewPage<TModel>类型的实例化。
下面的例子是向View中注入一个属性,用于在页面中便捷地读取资源文件信息。
1 2 3 4 5 | public abstract class LocalizableViewPage<TModel> : WebViewPage<TModel> { [Inject] public ResourceReader ResourceReader { get ; set ; } } |
1 2 3 4 5 6 7 | public class DefaultResourceReader : ResourceReader { public override string GetString( string name) { return Resources.ResourceManager.GetString(name); } } |
在View 顶部 添加命令 @inherits LocalizableViewPage<>
1 2 3 4 5 6 7 8 9 | @inherits LocalizableViewPage< object > < html > < head > < title ></ title > </ head > < body > < h2 >@ResourceReader.GetString("HelloWorld")</ h2 > </ body > </ html > |
或者修改View文件夹下的Web.config配置
1 2 3 4 5 6 7 8 9 10 11 12 13 | < system.web.webPages.razor > < host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> < pages pageBaseType="LocalizableViewPage"> < namespaces > < add namespace="System.Web.Mvc" /> < add namespace="System.Web.Mvc.Ajax" /> < add namespace="System.Web.Mvc.Html" /> < add namespace="System.Web.Optimization"/> < add namespace="System.Web.Routing" /> < add namespace="MvcApp" /> </ namespaces > </ pages > </ system.web.webPages.razor > |
5.2、 扩展RazorViewEngine
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class MyViewEngine : RazorViewEngine { public MyViewEngine() { ViewLocationFormats = new [] { "~/Views/{1}/{0}.cshtml" , "~/Views/Shared/{0}.cshtml" , "~/Views/Areas/{1}/{0}.cshtml" }; AreaViewLocationFormats = new [] { "~/Areas/{2}/Views/{1}/{0}.cshtml" , "~/Areas/{2}/Views/Shared/{0}.cshtml" , "~/Areas/{2}/Views/Config/{1}/{0}.cshtml" , "~/Areas/{2}/Views/User/{1}/{0}.cshtml" , "~/Areas/{2}/Views/Case/{1}/{0}.cshtml" , "~/Areas/{2}/Views/Config/Shared/{0}.cshtml" , "~/Areas/{2}/Views/User/Shared/{0}.cshtml" , "~/Areas/{2}/Views/Case/Shared/{0}.cshtml" }; } public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { return base .FindView(controllerContext, viewName, masterName, useCache); } } ViewEngines.Engines.Clear(); ViewEngines.Engines.Add( new MyViewEngine()); |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!