autofac解析Mvc和Webapi的坑


  我們在項目中很早就開始使用autofac,也以為知道與mvc和webapi集成的做法。

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中通過構造函數注入,這不必多說。

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

曾經在項目中看到過有人通過把Container設區全局靜態變量來獲得對象容器。這個方式在Local的情況下,不會有太大問題。在Mvc中,容器DependencyResolver.Current本身也是通過盡量變量來實現的。

    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一個服務,那么如何來指定容器呢?

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

    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的容器

    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來獲取的對象,竟然不等於構造函數解析出來的對象,有點毀三觀。說明它並不是當前上下文的對象,也就是說這個對象的生命周期不在控制范圍內。

那么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

希望對你有幫助,如有錯誤,歡迎指出!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM