.Net Core中Autofac的使用方法

Autofac是.NET里IOC(Inversion of Control)容器的一种,同类的框架还有Spring.NET,Unity,Castle等。可以通过NuGet方式将其添加到项目中使用。

Autofac官方网站:http://autofac.org/

一、安装Autofac

二、项目结构及基础代码

public interface IPlayService
{
    string Play();
}

public class PlayPianoService : IPlayService
{
    public string Play()
    {
        return "弹钢琴";
    }
}

 三、配置Autofac

1、将Startup.cs中ConfigureServices()的返回值改为IServiceProvider
2、创建一个返回IServiceProvider的方法,用来注册Autofac并提供ConfigureServices()所需的返回值
3、为了避免Startup.cs中代码过于混乱,我单独用AutofacComponent类来注册Autofac
4、为了避免组件过于复杂,我们可以根据不同功能创建多个Module类

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(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
{
    /// <summary>
    /// 注册Autofac组件
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceProvider Register(IServiceCollection services)
    {
        //实例化Autofac容器
        ContainerBuilder builder = new ContainerBuilder();
        //将collection中的服务填充到Autofac
        builder.Populate(services);
        //注册InstanceModule组件
        builder.RegisterModule<InstanceModule>();
        //创建容器
        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<PlayPianoService>();
    }
}

至此Autofac的基本配置就已经完毕了。

四、注册基本类型暴露基本类型

通过builder.RegisterType<PlayPianoService>();注册基本类型后,只能通过基本类型来注入,不能通过对应的接口类型注入,否则会报错。Autofac虽然支持这种用法,但在实际开发过程中并不常用,因为暴露出来的是基本类型,所以注入的依赖(PlayPianoService)与注入到的类(InstanceRegisterController)之间有很高的耦合度,实际中通过As、Named、Keyed暴露出接口更常用些,这个在后面会讲到。

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
{
    /// <summary>
    /// 根据程序集名称获取程序集
    /// </summary>
    /// <param name="AssemblyName">程序集名称</param>
    /// <returns></returns>
    public static Assembly GetAssemblyByName(String AssemblyName)
    {
        return Assembly.Load(AssemblyName);
    }
}
public class AutofacComponent
{
    /// <summary>
    /// 注册Autofac组件
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceProvider Register(IServiceCollection services)
    {
        //实例化Autofac容器
        ContainerBuilder builder = new ContainerBuilder();
        //将collection中的服务填充到Autofac
        builder.Populate(services);
        //注册ServiceModule组件[第五步新添加]
        builder.RegisterModule<ServiceModule>();
        //注册RepositoryModule组件[第五步新添加]
        builder.RegisterModule<RepositoryModule>();
        //注册InstanceModule组件[第三步已注册]
        builder.RegisterModule<InstanceModule>();
        //创建容器
        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<AlipayService>().Named<IPayService>(typeof(AlipayService).Name);
        builder.RegisterType<WxpayService>().Named<IPayService>(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<IPayService>(typeof(AlipayService).Name);
        wxpayService = this.componentContext.ResolveNamed<IPayService>(typeof(WxpayService).Name);
    }

    public IActionResult Index()
    {
        ViewData["walk"] = personService.Walk();
        ViewData["pay"] = payService.Pay();
        ViewData["componentAlipay"] = alipayService.Pay();
        ViewData["componentWxpay"] = wxpayService.Pay();
        return View();
    }
}

七、As、Named、Keyed三种注册方式

  • 当接口只有一种实现,或接口有多种实现但只使用最后注册的那个,可以用As
  • 当接口有多种实现,而且至少需要使用其中的两种实现时用Named和Keyed
  • Named()指定的参数只能是string,Keyed()指定的参数是object,当我们想把枚举当作名字是只能用Keyed()。
  • 无论是As、Named还是Keyed都可达到注册目的,但从功能上来讲,Autofac的设计者推荐第三种。如果使用Keyed方法注册类,可通过Autofac内置索引来查找指定的值,这个索引的类型是IIndex<Tkey,TValue>,其他两种注册方法没有这样的索引查找功能。
class Program
{
    private static IContainer container;

    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        builder.RegisterType<ConsoleOutput>().As<IOutput>();
        builder.RegisterType<TodayWriter>().Named<IDateWriter>(typeof(TodayWriter).Name);
        builder.RegisterType<YesterdayWriter>().Keyed<IDateWriter>(DateWriterType.Yesterday);
        container = builder.Build();
		
        IOutput output = container.Resolve<IOutput>();
        IDateWriter today = container.ResolveNamed<IDateWriter>(typeof(TodayWriter).Name);
        IDateWriter yesterday = container.ResolveKeyed<IDateWriter>(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<CookiePolicyOptions>(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<IControllerActivator, ServiceBasedControllerActivator>());

    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<AlipayService>().Named<IPayService>(typeof(AlipayService).Name);
        builder.RegisterType<WxpayService>().Named<IPayService>(typeof(WxpayService).Name);
        //注入DogService构造方法所需的参数
        builder.RegisterType<DogService>().As<IAnimalService>().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();
    }
}

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

changuncle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值