.Net Core中使用Autofac替换自带的DI容器的示例

 更新时间:2021年9月22日 09:59  点击:1674

为什么叫浅谈呢?就是字面上的意思,讲得比较浅,又不是不能用(这样是不对的)!!!

Aufofac大家都不陌生了,说是.Net生态下最优秀的IOC框架那是一点都过分。用的人多了,使用教程也十分丰富,官网教程也比较详细(如果英文功底还不错的话)。

那我为什么还要写这样一篇博客呢,一是用作学习笔记,二就是闲的。

废话不多说,开始正文

项目创建

云创建一个.Net Core Api项目,然后再添加一个类库,大概就是下面这样的结构:

新建一个类库项目,分别添加一个接口文件与类文件:

就这样,我们的演示方案就搭建完成了,下面就到了演示阶段。

方案演示

原始方案

俗话说的好,没有对象 new 一个就对了:

[HttpGet]
public string Original()
{
    IUserService userService = new UserService();
    return userService.GetName("Original");
}

结果当然是没问题的:

.Net Core自带DI

微软给我们提供的 DI 解决方案。如果是小项目,需要注入的服务不多,简直无敌好用,缺点就是不能批量注入,下面我们来复习一下:

先在 Startup 里面的 ConfigureServices 方法内注入(默认且只能构造函数注入)

services.AddScoped<IUserService, UserService>();

然后在控制器中拿到刚才注入的服务:

 public class DefaultController : ControllerBase
  {
      private readonly IUserService userService;
      public DefaultController(IUserService _userService)
      {
          this.userService = _userService;
      } 
     [HttpGet]
     public string CoreDI()
     {
         return userService.GetName("CoreDI");
     }
 }

很显然,一点问题都没有:

Autofac

注意事项说在前面:

在 .Net Core2 中一般是把StartupConfigureServices方法返回值类型改为IServiceProvider,然后通过构建Autofac容器并注入服务后返回。

在 .Net Core3.0之后,集成方式做了部分调整

下面演示的版本是.Net Core 3.1,也就是调整后的版本。

1、先引用 Autofac 的包,看看这下载次数

2、在 Program 中改用 Autofac 来实现依赖注入

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        // 就是这句
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

3、添加我们自定义的 Autofac 注册类,并注册我们需要的服务(默认构造函数注入,支持属性注入)

public class AutofacModuleRegister : Autofac.Module
{
    //重写Autofac管道Load方法,在这里注册注入
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<UserService>().As<IUserService>();
   }
}

4、在 Startup 类中添加方法:ConfigureContainer,

public void ConfigureContainer(ContainerBuilder builder)
{
    // 直接用Autofac注册我们自定义的 
    builder.RegisterModule(new AutofacModuleRegister());
}

5、大功告成,控制器内的方法甚至不用去改

public class DefaultController : ControllerBase
 {
     private readonly IUserService userService;
     public DefaultController(IUserService _userService)
     {
         this.userService = _userService;
     }

     [HttpGet]
     public string Autofac()
     {
         return userService.GetName("Autofac");
     }
 }

演示到这里就结束了,是不是感觉 Autofac 比自带的 DI 还要麻烦。其实不然,下面我们就来看看 Autofac 对比自带 DI 的一些特有特性。

不同的特性

批量注入

之前的项目我们有了用户 UserService,需求更新,加入了商品(ProductService),有了商品那又怎么能少得了订单(OrderService),那后面是不是还得有售后、物流、仓库、营销......

如果是.Net Core 自带的注入框架,那就只能不停的:

services.AddScoped<IProductService, ProductService>();
services.AddScoped<IOrderService, OrderService>();
......

这时候,Autofac 的好处就体现出来了:批量注入。

我们先回到上面的:AutofacModuleRegister 类,加入下面这段代码:

// 服务项目程序集
Assembly service = Assembly.Load("XXX.Service");
// 服务接口项目程序集
Assembly iservice = Assembly.Load("XXX.IService");
builder.RegisterAssemblyTypes(service, iservice)
    .Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract)
    .InstancePerLifetimeScope()
    .AsImplementedInterfaces();

上面的代码就是批量注入 XXX.Service 与 XXX.IService 项目下的服务与接口。

注意:如果需要注入的服务没有 interfac ,那么builder.RegisterAssemblyTypes 就只需要传一个程序集就OK了。如果服务与接口同在一个项目,那也是要传两个程序集的哦。

然后我们在控制器去通过构造函数获取注入的实例:

private readonly IUserService userService;
private readonly IProductService productService;

public DefaultController(IUserService _userService, IProductService _productService)
{
    this.userService = _userService;
    this.productService = _productService;
}

再对之前的 Autofac 接口添油加醋:

[HttpGet]
public string Autofac()
{
    var name = userService.GetName("Autofac");
    return productService.Buy(name, "批量注入");
}

结果自然是没有问题的,如果后续需要加入其它服务都不用再单独注入了,是不是优点就体现出来了。批量注入还有一些其它的玩法,比如筛选类名,筛选父类等。

属性注入

.Net Core 自带的 DI 框架与 Autofac 默认都是构造函数注入,官方建议也是构造函数注入。

但是有些同学可能就不喜欢构造函数注入,再加上有些场景确实不适合构造函数注入(比如基类实体),所以 Autofac 也支持属性注入,下面我们来看看使用方法,在之前批量注入的基础上,我们简单改造一下:

Assembly service = Assembly.Load("Autofac.Service");
Assembly iservice = Assembly.Load("Autofac.Service");
builder.RegisterAssemblyTypes(service, iservice)
    .Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract)
    .InstancePerLifetimeScope()
    .AsImplementedInterfaces()
    .PropertiesAutowired(); // 属性注入

对比构造函数注入,属性注入就多追加了PropertiesAutowired() 函数,控制器内修改:

public IUserService userService { get; set; }
public IProductService productService { get; set; }

注意:属性注入记得将属性的访问修饰符改为注册类可访问的修饰符,否则会注入失败。

下面我们来看看使用效果:

咦,怎么会空引用呢?原因大概就是 Controller 是由Mvc 模块管理的,不在 IOC 容器内,所以在 Controller 中无法使用 Autofac 注入的实例。

那为什么构造函数注入的时候又可以呢?大概或许可能他们都是构造函数注入吧...

为什么是大概呢?因为我暂时也没有具体去深入研究到底是什么原因导致的,如果有一天,我想起来去研究了并且有结果了,我会在这里补上。

我们先解决上面的问题先,在 Startup 的 ConfigureServices 方法底部加入如下代码:

// 使用 ServiceBasedControllerActivator 替换 DefaultControllerActivator;
// Controller 默认是由 Mvc 模块管理的,不在 Ioc 容器中。替换之后,将放在 Ioc 容器中。
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

然后回到我们的AutofacModuleRegister 注入 Controller:

builder.RegisterTypes(GetAssemblyTypes<Startup>(type => typeof(ControllerBase).IsAssignableFrom(type)))
                .PropertiesAutowired();

这样处理完后,属性注入就Ok了。

存储并提取容器实例

我们在之前项目的基础上添加两个项目 Common 与 Entities,存放公共类与实体类。

我们需要在实体类里面使用到 Common 项目中的某个类,结构如下:

// 基类实体   public class BaseEntity
    {
        public Class1 common_Class1 { get; set; }

        public string CreateId { get; set; }

        public void Create()
        {
            this.CreateId = common_Class1.getCurrentUserId();
        }
    }
    // 公共类
    public class Class1
    {
        public string getCurrentUserId()
        {
            return Guid.NewGuid().ToString();
        }
    }

从上面的接口中我们可以看到,我需要将 Class1 通过属性注入到容器中:

builder.RegisterType<Class1>().PropertiesAutowired().InstancePerLifetimeScope();

我们先在 Controller 中看看效果:

public Class1 class1 { get; set; }

[HttpGet]
public string Autofac()
{
    return class1.getCurrentUserId();
}

很显然结果是没问题的:

那我们再到 BaseEntity 中去试试看:

咦,又出现空引用,注入失败了。其实这个问题很明显,我们使用的是 new 来实例化的 BaseEntity对象,没有遵循容器实例使用规则,自然就无法使用容器中的实例了。

大家可以自己试一下,将 new 改为属性注入就没问题了,但是这种方案并不友好,下面要说的是另一种方案。

我们再新添加一个公共类:ContainerHelper,并声明一个属性用来存储容器的实例:

public static class ContainerHelper
{
    public static ILifetimeScope ContainerBuilder { get; set; }
}

然后回到 Startup 中,在Configure 方法的底部加入如下代码:

ContainerHelper.ContainerBuilder = app.ApplicationServices.CreateScope().ServiceProvider.GetAutofacRoot();

再回到实体类中去使用:

public void Create()
{
    if (common_Class1 == null)
    {
        using (var scope = ContainerHelper.ContainerBuilder.BeginLifetimeScope())
        {
            common_Class1 = scope.Resolve<Class1>();
        }
    }
    this.CreateId = common_Class1.getCurrentUserId();
}

Autofac 的替换方案暂时就写到这里了,后续如果有新的理解或心得会再做修改,浅谈嘛就真的是浅谈,有错误或补充的地方请大家不吝赐教。

源码这里就不提供了,大家有耐心的可以跟着手敲一遍,虽然对理解没啥作用,但能使记忆更深刻一点。

到此这篇关于.Net Core中使用Autofac替换自带的DI容器的示例的文章就介绍到这了,更多相关.Net Core Autofac替换DI容器内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • php批量替换内容或指定目录下所有文件内容

    要替换字符串中的内容我们只要利用php相关函数,如strstr,str_replace,正则表达式了,那么我们要替换目录所有文件的内容就需要先遍历目录再打开文件再利用上面讲的函数替...2016-11-25
  • Win10 IIS 安装.net 4.5的方法

    这篇文章主要介绍了Win10 IIS 安装及.net 4.5及Win10安装IIS并配置ASP.NET 4.0的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • .net数据库操作框架SqlSugar的简单入门

    这篇文章主要介绍了.net数据库操作框架SqlSugar的简单入门,帮助大家更好的理解和学习使用.net技术,感兴趣的朋友可以了解下...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • jQuery实现滑动页面固定顶部显示(可根据显示位置消失与替换)

    本文实例讲述了jQuery实现滑动页面固定顶部显示(可根据显示位置消失与替换)。分享给大家供大家参考,具体如下:这里介绍的jQuery拉动页面固定顶部显示,及自动消失效果,可能ie浏览器下有问题,不过火狐什么的都可以运行看效果...2015-10-30
  • 用golang如何替换某个文件中的字符串

    这篇文章主要介绍了用golang实现替换某个文件中的字符串操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-25
  • .NET C#利用ZXing生成、识别二维码/条形码

    ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。这篇文章主要给大家介绍了.NET C#利用ZXing生成、识别二维码/条形码的方法,文中给出了详细的示例代码,有需要的朋友们可以参考借鉴。...2020-06-25
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • C#使用Ado.Net更新和添加数据到Excel表格的方法

    这篇文章主要介绍了C#使用Ado.Net更新和添加数据到Excel表格的方法,较为详细的分析了OLEDB的原理与使用技巧,可实现较为方便的操作Excel数据,需要的朋友可以参考下...2020-06-25
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
  • Idea使用正则表达式批量替换字符串的方法

    这篇文章给大家介绍了Idea使用正则表达式批量替换字符串的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...2021-07-21
  • ASP.NET单选按钮控件RadioButton常用属性和方法介绍

    RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22
  • ASP.NET 2.0中的数据操作:使用两个DropDownList过滤的主/从报表

    在前面的指南中我们研究了如何显示一个简单的主/从报表, 该报表使用DropDownList和GridView控件, DropDownList填充类别,GridView显示选定类别的产品. 这类报表用于显示具有...2016-05-19
  • 详解.NET Core 使用HttpClient SSL请求出错的解决办法

    这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22