剖析ASP.NET MVC的DependencyResolver組件_實用技巧

來源:互聯網
上載者:User

一、前言

  DependencyResolver是MVC中一個重要的組件,從名字可以看出,它負責依賴對象的解析,可以說它是MVC架構內部使用的一個IOC容器。MVC內部很多個物件的建立都是通過它完成的,或許我們平時沒有直接用到它,但是如果你在使用unity、autofac,或者在看一些開源項目時,總會看到它的身影。接下來就讓我們看一下這個組件是如何工作的。

二、通過Controller的啟用理解DependencyResolver的工作過程

  這裡先插一個題外話,經常會有面試問:asp.net 幾個核心對象是什嗎?一般人都會回答:Server、Request、Response、Session、Cookie這些。但我的回答會是HttpApplication、HttpHandler和HttpModule,這才是管道模型中的核心類型,整個asp.net的處理流程和可擴充性也都是建立在這幾個對象上的。

  回到主題,asp.net請求都是交給HttpHandler處理的,對於MVC來說,是交給一個MvcHandler,它負責啟用Controller,如果你不知道為什麼,請看這裡。在這裡我們直接定位到MvcHandler的PR方法:

protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state){  IController controller;  IControllerFactory factory;  ProcessRequestInit(httpContext, out controller, out factory);   //其它操作  //調用 controller.Execute方法} private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory){  HttpContext currentContext = HttpContext.Current;   //從路由擷取controller名稱  string controllerName = RequestContext.RouteData.GetRequiredString("controller");   //通過ControllerBuilder擷取ControllerFactory,預設就是DefaultControllerFactory  factory = ControllerBuilder.GetControllerFactory();   //通過ControllerFactory擷取Controller對象  controller = factory.CreateController(RequestContext, controllerName);}

  ControllerFactory故名思議就是用於建立Controller的,我們也可以自己實現IControllerFactory,參與Controller的啟用過程,具體是在全域調用ControllerBuilder.Current.SetControllerFactory方法。我們這裡主要關注的是Controller的啟用過程,實際上它們的建立過程是相似的。預設使用的ControllerFactory是DefaultControllerFactory。DefaultControllerFactory的CreateController方法如下:  

public virtual IController CreateController(RequestContext requestContext, string controllerName){  //擷取Controller類型  Type controllerType = GetControllerType(requestContext, controllerName);   IController controller = GetControllerInstance(requestContext, controllerType);  return controller;} protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType){  return ControllerActivator.Create(requestContext, controllerType);}


  可以看到,它通過一個ControllerActivator來建立IController對象,預設使用的是DefaultControllerActivator。與ControllerFactory類似,我們可以實現IControllerActivator,參與Controller的啟用過程,具體是將ControllerActivator作為DefaultConrtollerFactory建構函式參數,然後再在全域調用ControllerBuilder.Current.SetControllerFactory方法。可以看到MVC的Controller啟用過程是很靈活的,它提供多種方式讓我們自訂啟用過程。DefaultControllerActivator定義如下:

private class DefaultControllerActivator : IControllerActivator{  private Func<IDependencyResolver> _resolverThunk;   public DefaultControllerActivator()    : this(null)  {  }   public DefaultControllerActivator(IDependencyResolver resolver)  {    if (resolver == null)    {      _resolverThunk = () => DependencyResolver.Current;    }    else    {      _resolverThunk = () => resolver;    }  }   public IController Create(RequestContext requestContext, Type controllerType)  {    try    {      return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));    }    catch (Exception ex)    {    }  }}


  這裡的_resolverThunk是一個用於擷取IDepencyResolver對象的委託,實際獲得的是DependencyResolver.Current。我們也可以自己實現IDependencyResolver,參與Controller的啟用過程,具體是在全域調用DependencyResolver的靜態方法SetResolver方法。需要注意的是這裡的DependencyResolver類型(這裡是類型,而其它地方提到的DependencyResolver都是組件的意思)並沒有實現IDependencyResolver介面,我覺得將它命名為DependencyResolverContainer會更合適一些。IDepdencyResolver介面的定義如下:

public interface IDependencyResolver{  object GetService(Type serviceType);  IEnumerable<object> GetServices(Type serviceType);}

  預設DependencyResolver.Current使用的是DefaultDependencyResolver類型,這裡又和ControllerFactory和ControllerActivator的設計一樣了,如果我們自訂,那麼就使用,否則就使用預設的。DefaultDependencyResolver定義如下:

private class DefaultDependencyResolver : IDependencyResolver{  public object GetService(Type serviceType)  {    if (serviceType.IsInterface || serviceType.IsAbstract)    {      return null;    }     try    {      //如果Controller Type建立Controller執行個體對象      return Activator.CreateInstance(serviceType);    }    catch    {      return null;    }  }   public IEnumerable<object> GetServices(Type serviceType)  {    return Enumerable.Empty<object>();  }}

  可以看到,MVC會將Controller對象的建立通過DependencyResolver完成。將對象的建立通過DependencyResolver完成的好處是可以降低對象間的耦合度;另外,通過實現IDependencyResolver介面,我們可以完全控制對象的建立過程,例如將對象的依賴關係轉移到設定檔中等等。

  通過上面我們還知道了有三種預設類型:DefaultControllerFactory、DefaultControllerActivator和DefaultDependencyResolver,分別對應三個介面:IControllerFactory、IControllerActivator、IDependencyResolver。它們的設計是類似的,都是提供給外部一個介面,如果外部自己實現了這個過程,那麼就使用,否則用預設的。實際上這也是我們參與Controller啟用過程的三種做法。

三、實現IDependencyResolver介面

  接下來通過一個例子證明上面的過程。我們要實現的需求是通過實現IDependencyResolver介面,實現Controller建構函式注入服務。如:

public class HomeController : Controller{        private IUserService _service;  public HomeController(IUserService service)  {    _service = service;  }   public ActionResult Index()  {    return Content(_service.GetUserName());  }}


  HomeController只依賴於IUserService介面,不依賴於具體對象。

  接下來我們實現IDependencyResolver介面,依賴注入的實現方式有很多種,這裡我們使用Unity。如下:

public class UnityDependencyResolver : IDependencyResolver{  public object GetService(Type serviceType)  {    if(serviceType == null)    {      throw new ArgumentNullException("serviceType");    }          return (serviceType.IsClass && !serviceType.IsAbstract)      || Ioc.IsRegistered(serviceType) ? Ioc.GetService(serviceType) : null;  }   public IEnumerable<object> GetServices(Type serviceType)  {    if (serviceType == null)    {      throw new ArgumentNullException("serviceType");    }    return (serviceType.IsClass && !serviceType.IsAbstract)      || Ioc.IsRegistered(serviceType) ? Ioc.GetServices(serviceType) : null;  }}

  這裡需要判斷 (serviceType.IsClass && !serviceType.IsAbstract) || Ioc.IsRegistered(serviceType) 原因是我們前面說過的,MVC內部很多個物件都是通過DependencyResolver組件建立的,如上面的IConrtollerFactoy,所以這裡我們只負責對登入的類型或類(非抽象類別)進行解析。

  Ioc類在這裡很簡單,如下:

public class Ioc{  private static IUnityContainer _container = new UnityContainer();   public static void RegisterType<TFrom,TTo>()    where TTo : TFrom  {          _container.RegisterType<TFrom, TTo>();  }   public static object GetService(Type type)  {                return _container.Resolve(type);  }   public static IEnumerable<object> GetServices(Type type)  {    return _container.ResolveAll(type);  }   public static bool IsRegistered(Type type)  {    return _container.IsRegistered(type);  }}

接著,在Application_Start方法中,註冊Service和設定IocDependencyResolver:

Ioc.RegisterType<IUserService, UserService>();
DependencyResolver.SetResolver(new IocDependencyResolver());
  運行就可以看到HomeController建構函式的IUserService就是UserService類型了。

四、總結

   實際上,上面的例子我們也可以用實現IControllerFactory或者IControllerActivator達到同樣的目的,但使用IDependencyResolver會更簡單一點,而且大部分的IOC架構都已經提供了這樣的功能。例如上面UnityDependencyResolver根本不用自己定義,Unity for MVC 已經有這麼一個類型了,直接使用即可。如果使用Autofac的話可以是:DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

以上就是本文的全部內容,希望對大家的學習有所協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.