我們在項目中很早就開始使用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
希望對你有幫助,如有錯誤,歡迎指出!