autofac解析Mvc和Webapi的坑

  咱們在項目中很早就開始使用autofac,也覺得知道與mvc和webapi集成的作法。web

var builder = new ContainerBuilder();

// Mvc Register
builder.RegisterControllers(Assembly.GetExecutingAssembly()).AsSelf().PropertiesAutowired();
builder.RegisterFilterProvider();
builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();

//WebApi Register
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).AsSelf().PropertiesAutowired();
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
builder.RegisterWebApiModelBinderProvider();

var container = builder.Build();

// Set the dependency resolver for Web API.
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;

// Set the dependency resolver for MVC.
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);

在實際Controller和ApiController中經過構造函數注入,這沒必要多說。api

可是,在實際項目需求的時候,有些地方不方便使用構造函數,或者說就要使用服務定位IContainer.Resolve(ServiceType)的方式來得到服務的實例。mvc

曾經在項目中看到過有人經過把Container設區全局靜態變量來得到對象容器。這個方式在Local的狀況下,不會有太大問題。在Mvc中,容器DependencyResolver.Current自己也是經過儘可能變量來實現的。ide

    public class DependencyResolver
    {
        public DependencyResolver();

        public static IDependencyResolver Current { get; }
    ...
    }

可是和C端不一樣的是,Web服務是基於請求的,autofac內部的InstancePerLifetimeScope,InstancePerHttpRequest,InstancePerApiRequest等都是基於每次請求的Scope,而靜態的Container明顯生命週期不符合。函數

因此咱們寫代碼的時候都是經過DependencyResolver.Current.GetService()和GlobalConfiguration.Configuration.DependencyResolver.GetService()來分別獲取Mvc和WebApi的對象。那麼問題來了,我有一段業務邏輯在BLL中,Mvc和WebApi能夠都調用到,其中須要Resolve一個服務,那麼如何來指定容器呢?ui

帶着問題,咱們先來看看DependencyResolver.Current和GlobalConfiguration.Configuration.DependencyResolver,經過一組實驗來對比一下:spa

    public class WebApiApplication : System.Web.HttpApplication
    {
        public static System.Web.Mvc.IDependencyResolver mvcResolver;
        public static System.Web.Http.Dependencies.IDependencyResolver apiResolver;

        protected void Application_Start()
        {
            ...
       builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
// Set the dependency resolver for Web API. var webApiResolver = new AutofacWebApiDependencyResolver(container); apiResolver = webApiResolver; GlobalConfiguration.Configuration.DependencyResolver = webApiResolver; // Set the dependency resolver for MVC. var resolver = new AutofacDependencyResolver(container); mvcResolver = resolver; DependencyResolver.SetResolver(mvcResolver); } }
   public interface IUserService
    {
    }

    public class UserService : IUserService
    {
    }

咱們分別定義了兩個靜態變量mvcResolver和apiResolver來存儲兩個不一樣的容器,並註冊了一組服務,指定其生命週期爲InstancePerLifetimeScope,先看看Mvc的容器code

    public class HomeController : Controller
    {
        IUserService _userservice;

        public HomeController(IUserService userService)
        {
            _userservice = userService;
            var a = DependencyResolver.Current.GetService<IUserService>();
            var b = WebApiApplication.mvcResolver.GetService<IUserService>();
            var c1 = ReferenceEquals(userService, a);   //true
            var c2 = ReferenceEquals(userService, b);   //true
            var c3 = ReferenceEquals(b, a);             //true
        }
    }

咱們在HomeContorller中經過三種不一樣方法來獲取對象,目的是三個對象都應該是同一個對象,結果也符合預期,再看看WebApi的:對象

    public class ValuesController : ApiController
    {
        IUserService _userservice;

        public ValuesController(IUserService userService)
        {
            _userservice = userService;
            var a = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUserService));
            var b = WebApiApplication.apiResolver.GetService(typeof(IUserService));
            var c1 = ReferenceEquals(userService, a);   //false
            var c2 = ReferenceEquals(userService, b);   //false
            var c3 = ReferenceEquals(b, a);             //true
        }
    }

發現經過GlobalConfiguration.Configuration.DependencyResolver來獲取的對象,居然不等於構造函數解析出來的對象,有點毀三觀。說明它並非當前上下文的對象,也就是說這個對象的生命週期不在控制範圍內。blog

那麼Mvc和WebApi可不能夠用同一個容器來指定呢?

咱們先來看看stackoverflow上的這篇文章:Is it possible to configure Autofac to work with ASP.NET MVC and ASP.NET Web Api

其實Mvc和WebApi分別是兩個獨立的依賴解析器,這點沒什麼問題,一個是System.Web.Mvc.IDependencyResolver另外一個是System.Web.Http.Dependencies.IDependencyResolver,兩個互相不串。

最後,一個很重要的對象來了,那就是Autofac.IComponentContext,它就是解析的上下文,經過它來解析的對象是符合當前上下文的,咱們再來看看以前的例子:

    public class HomeController : Controller
    {
        IUserService _userservice;

        public HomeController(IUserService userService, IComponentContext com)
        {
            _userservice = userService;
            var a = DependencyResolver.Current.GetService<IUserService>();
            var b = WebApiApplication.mvcResolver.GetService<IUserService>();
            var d = com.Resolve<IUserService>();
            var d1 = ReferenceEquals(userService, d);   //true
            var c1 = ReferenceEquals(userService, a);   //true
            var c2 = ReferenceEquals(userService, b);   //true
            var c3 = ReferenceEquals(b, a);             //true
        }
    }

WebApi:

    public class ValuesController : ApiController
    {
        IUserService _userservice;

        public ValuesController(IUserService userService, IComponentContext  com)
        {
            _userservice = userService;
            var a = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUserService));
            var b = WebApiApplication.apiResolver.GetService(typeof(IUserService));
            var d = com.Resolve<IUserService>();
            var d1 = ReferenceEquals(userService, d);   //true
            var c1 = ReferenceEquals(userService, a);   //false
            var c2 = ReferenceEquals(userService, b);   //false
            var c3 = ReferenceEquals(b, a);             //true
        }
    }

 

參考:

ASP .Net 4 Web Api RC + Autofac manual resolving

但願對你有幫助,若有錯誤,歡迎指出!

相關文章
相關標籤/搜索