Autofac是.NET里IOC(Inversion of Control)容器的一种,同类的框架还有Spring.NET,Unity,Castle等。可以通过NuGet方式将其添加到项目中使用。
Autofac官方网站:http://autofac.org/
public interface IPlayService
{
string Play();
}
public class PlayPianoService : IPlayService
{
public string Play()
{
return "弹钢琴";
}
}
1、将Startup.cs中ConfigureServices()的返回值改为IServiceProvider
2、创建一个返回IServiceProvider的方法,用来注册Autofac并提供ConfigureServices()所需的返回值
3、为了避免Startup.cs中代码过于混乱,我单独用AutofacComponent类来注册Autofac
4、为了避免组件过于复杂,我们可以根据不同功能创建多个Module类
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//注册Autofac组件
return AutofacComponent.Register(services);
}
public class AutofacComponent
{
///
/// 注册Autofac组件
///
///
///
public static IServiceProvider Register(IServiceCollection services)
{
//实例化Autofac容器
ContainerBuilder builder = new ContainerBuilder();
//将collection中的服务填充到Autofac
builder.Populate(services);
//注册InstanceModule组件
builder.RegisterModule();
//创建容器
IContainer container = builder.Build();
//第三方容器接管Core内置的DI容器
return new AutofacServiceProvider(container);
}
}
public class InstanceModule : Autofac.Module
{
//重写Autofac管道中的Load方法,在这里注入注册的内容
protected override void Load(ContainerBuilder builder)
{
//以普通类的形式注册PlayPianoService
builder.RegisterType();
}
}
至此Autofac的基本配置就已经完毕了。
通过builder.RegisterType
public class InstanceRegisterController : Controller
{
private PlayPianoService playPianoService;
//通过构造函数注入PlayPianoService类型的对象
public InstanceRegisterController(PlayPianoService playPianoService)
{
this.playPianoService = playPianoService;
}
public IActionResult Index()
{
ViewData["play"] = playPianoService.Play();
return View();
}
}
public class InstanceRegisterController : Controller
{
private IPlayService playService;
public InstanceRegisterController(IPlayService playService)
{
this.playService = playService;
}
public IActionResult Index()
{
ViewData["play"] = playService.Play();
return View();
}
}
如果我们注册完类型后暴露出来的是接口,那我们就可以随意改变注册的类型而不用修改注入到的类,这对于解耦来说还有点用。
暴露接口就只能通过接口类型来注入,不能通过具体的实现类型注入,否则会报错。
public class RepositoryModule : Autofac.Module
{
//重写Autofac管道中的Load方法,在这里注入注册的内容
protected override void Load(ContainerBuilder builder)
{
//注册Autofac.DAL中以Repository结尾的类
builder.RegisterAssemblyTypes(AutofacHelper.GetAssemblyByName("Autofac.DAL")).Where(a => a.Name.EndsWith("Repository")).AsImplementedInterfaces();
}
}
public class ServiceModule : Autofac.Module
{
//重写Autofac管道中的Load方法,在这里注入注册的内容
protected override void Load(ContainerBuilder builder)
{
//注册Autofac.BLL中以Service结尾的类
builder.RegisterAssemblyTypes(AutofacHelper.GetAssemblyByName("Autofac.BLL")).Where(a => a.Name.EndsWith("Service")).AsImplementedInterfaces();
}
}
public class AutofacHelper
{
///
/// 根据程序集名称获取程序集
///
/// 程序集名称
///
public static Assembly GetAssemblyByName(String AssemblyName)
{
return Assembly.Load(AssemblyName);
}
}
public class AutofacComponent
{
///
/// 注册Autofac组件
///
///
///
public static IServiceProvider Register(IServiceCollection services)
{
//实例化Autofac容器
ContainerBuilder builder = new ContainerBuilder();
//将collection中的服务填充到Autofac
builder.Populate(services);
//注册ServiceModule组件[第五步新添加]
builder.RegisterModule();
//注册RepositoryModule组件[第五步新添加]
builder.RegisterModule();
//注册InstanceModule组件[第三步已注册]
builder.RegisterModule();
//创建容器
IContainer container = builder.Build();
//第三方容器接管Core内置的DI容器
return new AutofacServiceProvider(container);
}
}
修改HomeController以构造方法的形式注入Service
public interface IPersonRepository
{
string Walk();
}
public class PersonRepository : IPersonRepository
{
public string Walk()
{
return "慢跑";
}
}
public interface IPersonService
{
string Walk();
}
public class PersonService : IPersonService
{
private IPersonRepository personRepository;
//通过构造函数注入IPersonRepository类型的对象
public PersonService(IPersonRepository personRepository)
{
this.personRepository = personRepository;
}
public string Walk()
{
return "靓仔" + personRepository.Walk();
}
}
public class HomeController : Controller
{
private IPersonService personService;
//通过构造函数注入IPersonService类型的对象
public HomeController(IPersonService personService)
{
this.personService = personService;
}
public IActionResult Index()
{
ViewData["walk"] = personService.Walk();
return View();
}
}
我现在在Service 中建三个类:IPayService、WxPayService、AliPayService,其中WxPayService、AliPayService都实现了接口IPayService。
public interface IPayService
{
string Pay();
}
public class AlipayService : IPayService
{
public string Pay()
{
return "支付宝支付";
}
}
public class WxpayService : IPayService
{
public string Pay()
{
return "微信支付";
}
}
创建CoverRegisterController来测试接口有多种实现时,依赖注入的效果。
public class CoverRegisterController : Controller
{
private IPersonService personService;
private IPayService payService;
public CoverRegisterController(IPersonService personService, IPayService payService)
{
this.personService = personService;
this.payService = payService;
}
public IActionResult Index()
{
ViewData["walk"] = personService.Walk();
ViewData["pay"] = payService.Pay();
return View();
}
}
IPayService最终注入的是WxpayService,因为在多个类实现同一接口的情况下,注册时后面注册的会覆盖前面注册的。如果我想得到支付宝支付,可以利用RegisterType()来实现,下面修改下ServiceModule组件。
public class ServiceModule : Autofac.Module
{
//重写Autofac管道中的Load方法,在这里注入注册的内容
protected override void Load(ContainerBuilder builder)
{
//注册Autofac.BLL中以Service结尾的类
builder.RegisterAssemblyTypes(AutofacHelper.GetAssemblyByName("Autofac.BLL"))
.Where(a => a.Name.EndsWith("Service")).AsImplementedInterfaces();
//通过Name单独注册AlipayService和WxpayService
builder.RegisterType().Named(typeof(AlipayService).Name);
builder.RegisterType().Named(typeof(WxpayService).Name);
}
}
创建CoverRegisterController,通过构造方法注入IComponentContext对象,最终利用IComponentContext对象来获取通过Name单独注册的AlipayService和WxpayService。
注意:IContainer继承了IComponentContext接口。
public class SingleRegisterController : Controller
{
private IPersonService personService;
private IPayService payService;
private IPayService alipayService;
private IPayService wxpayService;
private IComponentContext componentContext;
//当有多个类实现了IPayService时,后注册的会覆盖前边注册的
//动态注入的IPayService对象和IComponentContext对象相互独立,同时有效
public SingleRegisterController(IPersonService personService, IPayService payService, IComponentContext componentContext)
{
this.personService = personService;
this.payService = payService;
this.componentContext = componentContext;
//先用Named<>注册,再用ResolveNamed<>解析
alipayService = this.componentContext.ResolveNamed(typeof(AlipayService).Name);
wxpayService = this.componentContext.ResolveNamed(typeof(WxpayService).Name);
}
public IActionResult Index()
{
ViewData["walk"] = personService.Walk();
ViewData["pay"] = payService.Pay();
ViewData["componentAlipay"] = alipayService.Pay();
ViewData["componentWxpay"] = wxpayService.Pay();
return View();
}
}
class Program
{
private static IContainer container;
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType().As();
builder.RegisterType().Named(typeof(TodayWriter).Name);
builder.RegisterType().Keyed(DateWriterType.Yesterday);
container = builder.Build();
IOutput output = container.Resolve();
IDateWriter today = container.ResolveNamed(typeof(TodayWriter).Name);
IDateWriter yesterday = container.ResolveKeyed(DateWriterType.Yesterday);
output.Write("guoguo");
today.Write();
yesterday.Write();
System.Console.ReadKey();
}
}
除了常见的以构造方法的形式注入之外,还能够以属性的形式注入,下面讲解使用步骤。
public class ControllerModule : Autofac.Module
{
//重写Autofac管道中的Load方法,在这里注入注册的内容
//typeof(IFruit).IsAssignableFrom(typeof(Apple))-->true
//typeof(IFruit).IsAssignableFrom(typeof(IFruit))-->true
//typeof(Apple).IsAssignableFrom(typeof(IFruit))-->false
//typeof(Apple).IsAssignableFrom(typeof(Apple))-->true
protected override void Load(ContainerBuilder builder)
{
//Autofac属性注入[1]——注册系统所有Controller
Type IControllerType = typeof(Controller);
builder.RegisterAssemblyTypes(AutofacHelper.GetAssemblyByName("Autofac.Web"))
.Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired();
}
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//Autofac属性注入[2]——替换系统默认Controller创建器
services.Replace(ServiceDescriptor.Transient());
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//注册Autofac组件
return AutofacComponent.Register(services);
}
创建ComplexRegisterController来测试属性注入,结果证明属性注入和构造方法注入可以同时存在。
public class ComplexRegisterController : Controller
{
private IPersonService personService;
private IPayService payService;
//通过构造方法注入IPersonService类型的对象
public ComplexRegisterController(IPersonService personService, IPayService payService)
{
this.personService = personService;
this.payService = payService;
}
//通过属性注入IPayService类型的对象
public IPayService PayService { get; set; }
public IActionResult Index()
{
ViewData["walk"] = personService.Walk();
ViewData["prop_pay"] = PayService.Pay();
ViewData["ctor_pay"] = payService.Pay();
return View();
}
}
首先创建构造方法含参的类型,以备注册使用。
public interface IAnimalService
{
string Run();
}
public class DogService : IAnimalService
{
private string type;
private string name;
public DogService(string type, string name)
{
this.type = type;
this.name = name;
}
public string Run()
{
return $"[Dog]type={type},name={name}";
}
}
继续完善ServiceModule,单独注册含参的构造方法,一个参数就用一个WithParameter,多个参数就用连缀写法使用多个WithParameter对参数赋值。
注册含参的类型时,通过WithParameter提供的参数的类型和名字必须与构造方法形参的类型和名字完全一致,顺序可颠倒,注册时提供的参数只能多于构造方法所需的参数,少了则不能通过含参构造方法创建实例,而是去找无参构造方法,若没有无参构造方法就会报错。
public class ServiceModule : Autofac.Module
{
//重写Autofac管道中的Load方法,在这里注入注册的内容
protected override void Load(ContainerBuilder builder)
{
//注册Autofac.BLL中以Service结尾的类
builder.RegisterAssemblyTypes(AutofacHelper.GetAssemblyByName("Autofac.BLL"))
.Where(a => a.Name.EndsWith("Service")).AsImplementedInterfaces();
//通过Name单独注册AlipayService和WxpayService
builder.RegisterType().Named(typeof(AlipayService).Name);
builder.RegisterType().Named(typeof(WxpayService).Name);
//注入DogService构造方法所需的参数
builder.RegisterType().As().WithParameter("type", "small")
.WithParameter("name", "lala");
}
}
创建CtorParameterController来测试注入构造方法含参的类型。
public class CtorParameterController : Controller
{
private IAnimalService animalService;
//通过构造方法注入IAnimalService类型的对象
public CtorParameterController(IAnimalService animalService)
{
this.animalService = animalService;
}
public IActionResult Index()
{
ViewData["run"] = animalService.Run();
return View();
}
}