通常我们写程序时所有的对象都由我们手动的new出来,这样项目之间互相依赖,各个模块之间耦合严重,当需要修改为其他实现类时非常麻烦。通过IOC可以解除依赖,让代码结构看起来更加合理,并能够提升项目的稳定性和可用性。
Autofac是IOC(Inversion of Control,控制反转)容器的一种,使用IOC容器一般建议基于接口编程。在使用的时候声明接口类型的变量、属性是由容器负责赋值。接口、实现类一般也是定义在单独项目中以减少相互之间的耦合。
基于接口编程:把方法定义到接口中,通过实现类实现接口的方法;
安装Autofac:Install-Package Autofac
首先定义一个IAnimal接口和IRun接口,并分别定义Shout和Run方法:
namespace IService
{
public interface IAnimal
{
void Shout();
}
// 应该分开写
public interface IRun
{
void Run();
}
}
定义Dog、Student实现类实现如下接口:
namespace TestService
{
public class Dog : IAnimal, IRun
{
public void Run()
{
Console.WriteLine("小狗在跑");
}
public void Shout()
{
Console.WriteLine("汪汪汪");
}
}
// 同样要分开写
public class Student : IRun
{
public void Run()
{
Console.WriteLine("学生在跑");
}
}
}
首先看看Autofac如何注册一个对象的:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType().As(); // 注册实现类Dog 当请求IAnimal接口的时候返回Dog对象
IContainer container = builder.Build();
IAnimal animal = container.Resolve(); // 创建IAnimal实现类的对象
animal.Shout();
上例中builder.RegisterType
只是将Dog注册为IAnimal接口的类型对象,无法通过container.Resolve
方法调用Dog实现类Run方法。可以替换为builder.RegisterType
意思是将Dog注册为其所有实现的接口的接口类型对象。这样就可以同时调用Run的方法了。例:
IRun run = container.Resolve();
run.Run();
builder可以同时注册多个对象,如:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType().AsImplementedInterfaces(); // 注册Dog对象
builder.RegisterType().AsImplementedInterfaces(); // 注册Student对象
IContainer container = builder.Build();
IAnimal animal = container.Resolve();
animal.Shout();
IEnumerable runs = container.Resolve>();
foreach (var run in runs)
{
run.Run();
}
当我们需要注册的对象越多,以上写法就越显的多余,我们可以通过以下方法注册我们需要程序集中的对象:
Assembly[] assemblies = new Assembly[] { Assembly.Load("TestService") };
builder.RegisterAssemblyTypes(assemblies).AsImplementedInterfaces();
如果同一个接口有多个实现类,如IRun接口,container.Resolve
方法只会返回其中一个类的对象。 如果想返回多个实现类的对象,使用container.Resolve
获取通过foreach遍历即可。
如果一个实现类中定义了其他类型的接口属性,可以在注册的时候加上PropertiesAutowired(),PropertiesAutowired()会自动给属性进行“注入”。 如果可能有多个实现类,还可以声明IEnumerable类型的属性。被只有被Autofac 创建出来的对象才会被“属性自动注入”。
public class Dog : IAnimal
{
public IEnumerable Runs { get; set; }
public void Shout()
{
foreach (var run in Runs)
{
run.Run();
}
Console.WriteLine("汪汪汪");
}
}
/* ================================================== */
ContainerBuilder builder = new ContainerBuilder();
Assembly[] assemblies = new Assembly[] { Assembly.Load("TestService") };
builder.RegisterAssemblyTypes(assemblies).AsImplementedInterfaces().PropertiesAutowired();
IContainer container = builder.Build();
IAnimal animal = container.Resolve();
animal.Shout();
安装Autofac:Install-Package Autofac.Mvc5
首先构造一个Autofac容器的创建者:
// 构造Autofac容器的builder
var builder = new ContainerBuilder();
然后根据自己需要注册内容:
// 注入控制器
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
// 把程序集数组assemblies中所有非抽象的类型注入
Assembly[] assemblies = new Assembly[] { Assembly.Load("TestService") };
builder.RegisterAssemblyTypes(assemblies).Where(t => !t.IsAbstract).AsImplementedInterfaces()
.PropertiesAutowired();
// 注入特性
builder.RegisterFilterProvider();
// 注入ActionFilter
builder.RegisterAssemblyTypes(typeof(MvcApplication).Assembly)
.Where(t => typeof(ActionFilterAttribute).IsAssignableFrom(t) && !t.IsAbstract)
.PropertiesAutowired();
通过builder创建容器,并提供给MVC:
// 创建容器
IContainer container = builder.Build();
// 提供给MVC,这样当MVC框架创建Controller等对象时都是从Autofac获取
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
在Autofac容器中注入后,在使用时只用在Controller中声明Service属性即可,Autofac会自动进行“属性注入”。
public class DefaultController : Controller
{
public IService Service { get; set; }
}
当然,只有通过Autofac帮我们创建的对象并且PropertiesAutowired()后才能给我们自动进行属性的赋值。
有时候我们需要在没有进行注入的地方(如:TestHelper)获取注入的对象,可以通过以下方法:
public class TestHelper
{
public static void Test()
{
IService service = DependencyResolver.Current.GetService();
}
}
上面方法指的是在同一个线程中调用,当我们在Quartz等单独线程中,就无法通过DependencyResolver.Current.GetService
方式获取,需要通过下面的方式获取对象:
IService service;
var container = AutofacDependencyResolver.Current.ApplicationContainer;
using (container.BeginLifetimeScope())
{
service = container.Resolve();
}
InstancePerDependency()
:每次请求Resolve都会返回一个新的、唯一的实例;
SingleInstance()
:单例,每次都获取相同的共享实例(返回同一个对象);
InstancePerLifetimeScope()
: 每个生命周期一个实例;
InstancePerRequest()
: ASP.Net MVC 专用,每个请求一个对象;