閱讀770 返回首頁    go 阿裏雲 go 技術社區[雲棲]


ASP.NET MVC的Razor引擎:IoC在View激活過程中的應用

在《ASP.NET MVC的Razor引擎:RazorView》介紹BuildManagerCompiledView的時候,我們談到默認使用的ViewPageActivator使用當前注冊的DependencyResolver來完成對目標View的激活,這意味著我們可以通過注冊自定義DependencyResolver的方式實現基於IoC的View激活。本篇文章中我們將演示如何通過自定義View的方式實現與IoC框架Ninject的集成。[本文已經同步到《How ASP.NET MVC Works?》中]

我們定義了一個具有如下定義的NinjectDependencyResolver,它具有一個IKernel類型的隻讀屬性Kernel,該屬性在構造函數中被初始化為一個StandardKernel對象。對於實現的GetService和GetServices方法,我們直接調用Kernel的TryGet和GetAll返回指定類型的實例和實例列表。為了方便進行類型映射,我們定義了泛型的Register<TFrom,TTo>方法。

   1: public class NinjectDependencyResolver : IDependencyResolver
   2: {
   3:     public IKernel Kernel { get; private set; }
   4:     public NinjectDependencyResolver()
   5:     {
   6:         this.Kernel = new StandardKernel();
   7:     }
   8:     public void Register<TFrom, TTo>() where TTo: TFrom
   9:     {
  10:         this.Kernel.Bind<TFrom>().To<TTo>();
  11:     }
  12:     public object GetService(Type serviceType)
  13:     {
  14:         return this.Kernel.TryGet(serviceType);
  15:     }
  16:     public IEnumerable<object> GetServices(Type serviceType)
  17:     {
  18:         return this.Kernel.GetAll(serviceType);
  19:     }
  20: }

我們演示的是一個針對多語言支持的場景,為了讓View上輸出的一些內容隨著當前線程的UICulture而動態地變化,我們在一個ASP.NET MVC應用中定義如下一個讀取資源內容抽象類ResourceReader。這裏資源是一個寬泛的概念,並不對存儲方式作強製的約束,我們可以使用資源文件也可以使用數據庫來存儲資源內容。簡單起見,ResourceReader僅僅定義了一個唯一GetString方法獲取指定名稱的字符串。

   1: public abstract class ResourceReader
   2: {
   3:     public abstract string GetString(string name);
   4: }

我們默認采用資源文件來定義數據源,為此我們在項目中添加了兩個資源文件Resoures.resx(語言文化中性)和Resources.zh.resx(中文),並在資源文件中添加了如下圖所示的資源項(HelloWorld)。

image

然後我們創建如下一個默認的DefaultResourceReader,它默認讀取我們添加的資源文件來獲取GetString方法返回的字符串(靜態類型Resources是添加資源文件自動創建的類型)。

   1: public class DefaultResourceReader : ResourceReader
   2: {
   3:     public override string GetString(string name)
   4:     {
   5:         return Resources.ResourceManager.GetString(name);
   6:     }
   7: }

為了讓ResourceManager能夠應用到所有的View中,我們為整個應用的View創建了如下一個基類LocalizableViewPage<TModel>。該類型是WebViewPage<TModel>的子類,它具有一個類型為ResourceManager的屬性ResourceManager。由於該屬性上應用了Ninject.InjectAttribute特性,意味著該屬性會以“屬性注入”的方式被自動初始化。

   1: public abstract class LocalizableViewPage<TModel>: WebViewPage<TModel>
   2: {
   3:     
   4:     public ResourceReader ResourceReader { get;  set; }
   5: }

接下來我們定義了如下一個簡單的HomeController,其默認的Action方法Index中直接將對應的View呈現出來。

   1: public class HomeController : Controller
   2: {
   3:     public ActionResult Index()
   4:     {
   5:         return View();
   6:     }
   7: }

如下所示的是Action方法Index對應View的定義,我們使用@inherits指令讓動態編譯生成的View類型繼承自我們自定義的基類LocalizableViewPage<object>。我們直接調用ResourceReader屬性的GetString方法提取名稱為“HelloWorld”的字符串資源內容顯示出來。

   1: @inherits LocalizableViewPage<object>
   2: <html>
   3:     <head>
   4:         <title></title>
   5:     </head>
   6:     <body>
   7:         <h2>@ResourceReader.GetString("HelloWorld")</h2>
   8:     </body>
   9: </html>

我們采用基於URL的語言文化決定機製,即將語言文化的代碼置於請求URL中來決定希望采用的語言。為此我們在自動生成的RouteConfig類型中注冊了如下一個URL模板為“{culture}/{controller}/{action}”的路由對象。

   1: public class RouteConfig
   2: {
   3:     public static void RegisterRoutes(RouteCollection routes)
   4:     {
   5:         //其他操作
   6:         routes.MapRoute(
   7:             name        : "Default",
   8:             url         : "{culture}/{controller}/{action}",
   9:             defaults    : new {
  10:             culture        = "zh-CN",
  11:             controller     = "Home",
  12:             action         = "Index"}
  13:         );
  14:     }
  15: }

我們自定義的DefaultResourceReader能夠根據當前線程的UICulture選擇對應的資源文件,那麼我們隻需要根據請求地址指示的語言文件對當前線程的語言文件進行相應的設置即可。於是我們在Global.asax定義了如下一個Application_BeginRequest方法使HttpApplication的BeginRequest事件觸發的時候從請求地址中提取語言文化代碼,然後對當前線程的語言文化進行相應的設置。除此之外,針對NinjectDependencyResolver的注冊和ResourceReader與Default ResourceReader之間的映射關係定義在Application_Start方法中。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         NinjectDependencyResolver dependencyResovler = new NinjectDependencyResolver();
   7:         dependencyResovler.Register<ResourceReader, DefaultResourceReader>();
   8:         DependencyResolver.SetResolver(dependencyResovler);
   9:     }
  10:  
  11:     protected void Application_BeginRequest()
  12:     { 
  13:         HttpContextBase contextWrapper = new HttpContextWrapper(HttpContext.Current);
  14:         string culture = RouteTable.Routes.GetRouteData(contextWrapper).Values["culture"] as string;
  15:         if (!string.IsNullOrEmpty(culture))
  16:         {
  17:             try
  18:             {
  19:                 CultureInfo cultureInfo                 = new CultureInfo(culture);
  20:                 Thread.CurrentThread.CurrentCulture     = cultureInfo;
  21:                 Thread.CurrentThread.CurrentUICulture   = cultureInfo;
  22:             }
  23:             catch {}
  24:         }
  25:     }
  26: }

現在運行我們的程序,並通過地址指定采用的語言文化,我們可以發現呈選出來的內容與你指定的語言文化是一致的,具體的輸出效果如下圖所示。

image

ASP.NET MVC的Razor引擎:View編譯原理
ASP.NET MVC的Razor引擎:RazorView
ASP.NET MVC的Razor引擎:IoC在View激活過程中的應用
ASP.NET MVC的Razor引擎:RazorViewEngine


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-25 16:33:51

  上一篇:go  ASP.NET MVC的Razor引擎:RazorView
  下一篇:go  ASP.NET MVC的Razor引擎:RazorViewEngine