文章目录

  • 什么是依赖?
  • 什么是注入?
  • 为什么要反转?
  • 何为容器
  • 默认容器
  • Autofac框架
  • 框架使用
    • 1、NuGet引入包
    • 2、在ConfigureServices中创建容器对象,并返回IServiceProvider
  • Autofac注册组件
    • 反射注册
    • 实例注册
    • Lambda表达式注册
    • 泛型注册
    • 条件注册
    • 属性注入
    • 程序集注册
  • Autofac暴露服务
    • 默认暴露自身类型服务
    • 多个暴露服务类型
  • Autofac解析服务
    • 程序集注册指定暴露类型
    • 解析服务
    • 解析时传参
  • Autofac生命周期
    • 暂时性
    • 作用域内
    • 匹配作用域内
  • 来源

什么是依赖?

当一个类需要另一个类协作来完成工作的时候就产生了依赖。比如我们在AccountController这个控制器需要完成和用户相关的注册、登录 等事情。其中的登录我们由EF结合Idnetity来完成,所以我们封装了一个EFLoginService。这里AccountController就有一个ILoginService的依赖。

什么是注入?

依赖注入(Dependency injection,DI)是一种实现对象及其合作者或依赖项之间松散耦合的技术。

注入体现的是一个IOC(控制反转的的思想),把依赖的创建丢给其它人,自己只负责使用,其它人丢给你依赖的这个过程理解为注入。

        private readonly IAccountAppService _accountAppService;/// <summary>/// 构造/// </summary>/// <param name="accountAppService">账户中心接口</param>public AccountController(IAccountAppService accountAppService){_accountAppService = accountAppService;}

为什么要反转?

为了在业务变化的时候尽少改动代码可能造成的问题。
比如我们现在要把从EF中去验证登录改为从Redis去读,于是我们加了一个 RedisLoginService。这个时候我们只需要在原来注入的地方改一下就可以了。

// 用Redis来替换原来的EF登录 var controller = new AccountController(new RedisLoginService()); controller.Login(userName, password);

何为容器

上面我们在使用AccountController的时候,我们自己通过代码创建了一个IAccountServce的实例。想象一下,一个系统中如果有100个这样的地方,我们是不是要在100个地方做这样的事情? 控制是反转了,依赖的创建也移交到了外部。现在的问题是依赖太多,我们需要一个地方统一管理系统中所有的依赖,容器诞生了。
容器负责两件事情:

  • 绑定服务与实例之间的关系
  • 获取实例,并对实例进行管理(创建与销毁)

默认容器

ASP.NET Core 的底层设计支持和使用依赖注入。ASP.NET Core
应用程序可以利用内置的框架服务将它们注入到启动类Startup的方法中,并且应用程序服务ConfigureServices能够配置注入。

在内置的IoC容器中,在Startup.cs类文件ConfigureServices方法中,注入依赖方式如下:
public void ConfigureServices(IServiceCollection services)

{services.AddControllers();services.AddTransient<ITransientService, TransientService>();services.AddSingleton<ISingletonService, SingletonService>();services.AddScoped<IScopedService, ScopedService>();services.AddScoped<ITestService, TestService>();
}

Autofac框架

AutoFac是一个开源的轻量级的依赖注入容器,也是.net下比较流行的实现依赖注入的工具之一。

将Autofac整合到你的应用的基本流程如下:

  • 按照 控制反转 (IoC) 的思想构建你的应用.
  • 添加Autofac引用.
  • 在应用的 startup 处
  • 创建 ContainerBuilder.
  • 注册组件.
  • 创建容器,将其保存以备后续使用.
  • 应用执行阶段
  • 从容器中创建一个生命周期.
  • 在此生命周期作用域内解析组件实例.

框架使用

1、NuGet引入包

Install-Package Autofac -Version 4.8.1 Install-Package
Autofac.Extensions.DependencyInjection -Version 4.3.0

2、在ConfigureServices中创建容器对象,并返回IServiceProvider

一些需用到的方法:创建Runtime帮助获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;namespace Dianshi.P2P.Core.Client.WebApi.Auth
{[ApiExplorerSettings(IgnoreApi = true)]public class RuntimeHelper{/// <summary>/// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包/// </summary>/// <returns></returns>public static IList<Assembly> GetAllAssemblies(){List<Assembly> list = new List<Assembly>();var deps = DependencyContext.Default;//排除所有的系统程序集、Nuget下载包var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");foreach (var lib in libs){try{var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));list.Add(assembly);}catch (Exception ex){//}}return list;}public static Assembly GetAssembly(string assemblyName){return GetAllAssemblies().FirstOrDefault(f => f.FullName.Contains(assemblyName));}public static IList<Type> GetAllTypes(){List<Type> list = new List<Type>();foreach (var assembly in GetAllAssemblies()){var typeinfos = assembly.DefinedTypes;foreach (var typeinfo in typeinfos){list.Add(typeinfo.AsType());}}return list;}/// <summary>/// 根据AssemblyName获取所有的类/// </summary>/// <param name="assemblyName"></param>/// <returns></returns>public static IList<Type> GetTypesByAssembly(string assemblyName){List<Type> list = new List<Type>();var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName));var typeinfos = assembly.DefinedTypes;foreach (var typeinfo in typeinfos){list.Add(typeinfo.AsType());}return list;}public static Type GetImplementType(string typeName, Type baseInterfaceType){return GetAllTypes().FirstOrDefault(t =>{if (t.Name == typeName && t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)){var typeinfo = t.GetTypeInfo();return typeinfo.IsClass && !typeinfo.IsAbstract && !typeinfo.IsGenericType;}return false;});}}
}

然后

 #region 依赖注入var builder = new ContainerBuilder();//实例化容器//注册所有模块modulebuilder.RegisterAssemblyModules(Assembly.GetExecutingAssembly());//获取所有的程序集//var assemblys = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();var assemblys = RuntimeHelper.GetAllAssemblies().ToArray();//注册所有继承IDependency接口的类//builder.RegisterAssemblyTypes().Where(type => typeof(IDependency).IsAssignableFrom(type) && !type.IsAbstract);//注册仓储,所有IRepository接口到Repository的映射builder.RegisterAssemblyTypes(assemblys).Where(t => t.Name.EndsWith("Repository") && !t.Name.StartsWith("I")).AsImplementedInterfaces();//注册服务,所有IApplicationService到ApplicationService的映射builder.RegisterAssemblyTypes(assemblys).Where(t => t.Name.EndsWith("AppService") && !t.Name.StartsWith("I")).AsImplementedInterfaces();builder.Populate(services);this.ApplicationContainer = builder.Build();return new AutofacServiceProvider(this.ApplicationContainer); //第三方IOC接管 core内置DI容器 //return services.BuilderInterceptableServiceProvider(builder => builder.SetDynamicProxyFactory());#endregion

ConfigureServices的返回值(默认为void)为IServiceProvider

测试

 public ActionResult<string> Ioc(){//_accountAppService定义在《什么是注入?》章节下return _accountAppService.Login(new Application.Members.Accounts.Dto.LoginInput() { Password = "1", Phone = "2" });}

Autofac注册组件

通过创建 ContainerBuilder 来注册组件,并且告诉容器哪些组件,暴露了哪些服务。

使用 Register() 方法来注册实现:

ContainerBuilder 包含一组 Register() 注册方法,而组件暴露服务,可用使用 ContainerBuilder 上的 As() 方法。

即在容器初始化时候,向容器组件添加对象的操作过程。

通过梳理Autofac所有可用的注册组件方法,显示如下图展示的流程图。

反射注册

直接注册的组件必须是具体的类型,并可用暴露抽象和接口作为服务,但不能注册一个抽象和接口组件。

使用RegisterType()或者RegisterType(typeof(T))方法:

builder.RegisterType<TestService>().As<ITestService>();
// 或者
builder.RegisterType(typeof(TestService)).As(typeof(ITestService))

在多个构造函数时,如果需要,也可手动指定一个构造函数。

使用 UsingConstructor 方法和构造方法中代表参数类型的类型。

builder.RegisterType<TestService>().UsingConstructor(typeof(TransientService), typeof(SingletonService));

实例注册

提前生成对象的实例并加入容器,以供注册组件时使用。

使用RegisterInstance()方法

// new出一个对象注册:
var output = new StringWriter();
builder.RegisterInstance(output).As<TestService>();

如果单例中存在实例且需要在容器中被组件使用时,

builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

Lambda表达式注册

当组件创建不再是简单调用构造方法时,可用利用lambda表达式来实现一些常规反射无法实现的操作。

比如一些复杂参数注册,参数注入,以及选择参数值实现等。

builder.Register(x => new TransientService()).As<ITransientService>();
// 或者指定参数builder.Register(x => new TestService(x.Resolve<ITransientService>(), x.Resolve<IScopedService>(), x.Resolve<ISingletonService>())).As<ITestService>().InstancePerLifetimeScope();

泛型注册

支持泛型注册操作,使用 RegisterGeneric() 方法:

builder.RegisterGeneric(typeof(NHibernateRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

条件注册

在一些特殊场景,可能需要通过加上判断条件,来决定是否执行该条注册语句。

两种方法:

1、OnlyIf() - 提供一个表达式, 表示只有满足条件,才会执行语句。

builder.RegisterType<Manager>().As<IManager>().OnlyIf(reg =>reg.IsRegistered(new TypedService(typeof(IService))) &&reg.IsRegistered(new TypedService(typeof(HandlerB))));

2、IfNotRegistered() - 表示没有其他服务注册的情况下,就执行语句。
方法在 ContainerBuilder.Build() 时执行并且以实际组件注册的顺序执行。

builder.RegisterType<ServiceA>().As<IService>();
builder.RegisterType<ServiceB>().As<IService>().IfNotRegistered(typeof(IService));

属性注入

构造方法参数注入是一种传值给组件的首选的方法。

在构造函数中是直接使用服务类型作为参数,然后AutoFac解析该类时,就会去容器内部已存在的组件中查找,然后将匹配的对象注入到构造函数中去。

但你同样也可以使用属性方法注入来传值。

是将容器内对应的组件直接注入到类内的属性中去,在注册该属性所属类的时候,需要使用PropertiesAutowired()方法额外标注。

这里不讨论属性注入的好坏,也不做说明服务层属性怎么注入,只讨论说明控制器中属性如何实现注入

1、注册组件方法,并使用属性注入PropertiesAutowired()标注。

builder.RegisterType<TransientService>().As<ITransientService>().PropertiesAutowired();

2、在控制器中使用属性来接收, 其中注入属性必须标注为public

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{public ITransientService _transientService { get; set; }[HttpGet]public JsonResult Get(){var data1 =   _transientService.GetGuid();return new JsonResult(new {data1});}
}

程序集注册

当我们需要实现批量注册的时候,也可以使用程序集的方式来注册,这也是常用的方法。

可通过指定过滤类型,服务,扫描模块等方式来找到需要注册的组件。

var assemblies = Assembly.GetExecutingAssembly();builder.RegisterAssemblyTypes(assemblies)//程序集内所有具象类
.Where(c => c.Name.EndsWith("Service"))
.PublicOnly()//只要public访问权限的
.Where(cc => cc.IsClass)//只要class型(主要为了排除值和interface类型)
.AsImplementedInterfaces();//自动以其实现的所有接口类型暴露(包括IDisposable接口)

说明:

  • RegisterAssemblyTypes() :接收包含一个或多个程序集的数组作为参数
  • RegisterAssemblyModules() : 接收模块作为参数,进行模块扫描注册
  • PublicOnly() :指定公有方法被注册
  • Where() :要过滤注册的类型
  • Except() :要排除的类型
  • As() :反射出其实现的接口
  • AsImplementedInterfaces() : 自动以其实现的所有接口类型暴露(包括IDisposable接口)

Autofac暴露服务

上面提到了注册组件时, 我们得告诉Autofac, 组件暴露了哪些服务。

在上面注册实现中,大部分使用到了As() 方法。

当然,Autofac也提供了其他标注来暴露服务的方法。

默认暴露自身类型服务

常用的几种方法如下:

builder.RegisterType<CallLogger>();//不标注,默认以自身类型暴露服务
builder.RegisterType<CallLogger>().AsSelf();
builder.RegisterType<CallLogger>().As<CallLogger>();
builder.RegisterType<CallLogger>().As(typeof(CallLogger));

多个暴露服务类型

以其实现的接口(interface)暴露服务,暴露的类型可以是多个,比如CallLogger类实现了ILogger接口和ICallInterceptor接口。

暴露服务后, 可以解析基于该服务的组件了. 但请注意, 一旦将组件暴露为一个特定的服务, 默认的服务 (组件类型) 将被覆盖。

所以,为了防止被其他服务覆盖,可以使用 AsSelf() 方法。

Autofac解析服务

Copybuilder.RegisterType<CallLogger>().As<ILogger>().As<ICallInterceptor>().AsSelf();

这样你既可以实现组件暴露一系列特定的服务, 又可以让它暴露默认的服务。

程序集注册指定暴露类型

1、可通过指定接口类型暴露服务,使用As() 方法

publi void ConfigureContainer(ContainerBuilder builder)
{builder.RegisterAssemblyTypes(assemblies)//程序集内所有具象类.Where(cc =>cc.Name.EndsWith("Repository")|//筛选cc.Name.EndsWith("Service")).As(x=>x.GetInterfaces()[0])//反射出其实现的接口,并指定以其实现的第一个接口类型暴露
}

2、指定所有实现的接口类型进行暴露

使用AsImplementedInterfaces()函数实现,相当于一个类实现了几个接口(interface)就会暴露出几个服务,等价于上面连写多个As()的作用。

publi void ConfigureContainer(ContainerBuilder builder)
{builder.RegisterAssemblyTypes(asm).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces();//自动以其实现的所有接口类型暴露(包括IDisposable接口)
}

解析服务

在注册完组件并暴露相应的服务后, 可以从创建的容器或其生命周期中解析服务。

使用 Resolve() 方法来解析实现:

通过梳理Autofac所有可用的解析服务方法,显示如下图展示的流程图。

在 注册完组件并暴露相应的服务后, 你可以从创建的容器或其子 生命周期 中解析服务. 让我们使用 Resolve() 方法来实现:

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>().As<IService>();
var container = builder.Build();using(var scope = container.BeginLifetimeScope())
{var service = scope.Resolve<IService>();
}

解析时传参

当解析服务时, 需要传参,可以使用Resolve() 方法来接受可变长度的参数。

  • 可用参数类型

NamedParameter - 通过名称匹配目标参数

TypedParameter - 通过类型匹配目标参数 (需要匹配具体类型)

ResolvedParameter - 灵活的参数匹配

  • 反射组件的参数
var reader = scope.Resolve<ConfigReader>(new NamedParameter("configSectionName", "sectionName"));
  • Lambda表达式组件的参数
  • 不显式调用Resolve传参

Autofac生命周期


下面讲下AutoFac定义的几种生命周期作用域,并与.NET Core默认的生命周期作了简要的对比。

暂时性

每次在向服务容器进行请求时都会创建新的实例,相当于每次都new出一个。

注册方式:

使用InstancePerDependency()方法标注,如果不标注,这也是默认的选项。以下两种注册方法是等效的:

//不指定,默认就是瞬时的
builder.RegisterType<TransientService>().As<ITransientService>();//指定其生命周期域为瞬时
builder.RegisterType<TransientService>().As<ITransientService>().InstancePerDependency();

对比:

与默认的容器中自带的生命周期AddTransient相同,也是每次都是全新的实例。
使用AddTransient()注册:

services.AddTransient<ITransientService, TransientService>()

作用域内

在每次Web请求时被创建一次实例,生命周期横贯整次请求。即在每个生命周期作用域内是单例的。

注册方式:

使用InstancePerLifetimeScope()方法标识:

builder.RegisterType<ScopedService>().As<IScopedService>().InstancePerLifetimeScope();

对比:

与默认的容器中自带的生命周期AddScoped相同,.NET Core框架自带的容器全权接管了请求和生命周期作用域的创建,使用Scoped()可以实现相同的效果。
使用AddScoped()注册:

services.AddScoped<IScopedService, ScopedService>();

匹配作用域内

即每个匹配的生命周期作用域一个实例。
该类型其实是上面的“作用域内”的其中一种,可以对实例的共享有更加精准的控制.。我们通过允许给域“打标签”,只要在这个特定的标签域内就是单例的。

  • 注册

使用InstancePerMatchingLifetimeScope(string tagName)方法注册:

var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("myrequest");

当你开始一个生命周期时, 提供的标签值和它就关联起来了。

  • 解析
// myrequest标签子域一
using(var scope1 = container.BeginLifetimeScope("myrequest"))
{for(var i = 0; i < 100; i++){var w1 = scope1.Resolve<Worker>();using(var scope2 = scope1.BeginLifetimeScope()){var w2 = scope2.Resolve<Worker>();//解析了2次,但2次都是同一个实例(w1和w2指向同一个内存块Ⅰ)}}
}// //myrequest标签子域二
using(var scope3 = container.BeginLifetimeScope("myrequest"))
{for(var i = 0; i < 100; i++){//因为标签域内已注册过,所以可以解析成功var w3 = scope3.Resolve<Worker>();using(var scope4 = scope3.BeginLifetimeScope()){var w4 = scope4.Resolve<Worker>();}}
}

对比:

services.AddSingleton<IProductService, ProductService>();

来源

.NET Core API框架实战(五) 依赖注入 服务的注册与提供

Autofac 框架初识与应用

【C#】依赖注入及Autofac相关推荐

  1. 依赖注入容器Autofac的详解[转]

    依赖注入容器Autofac的详解 发表于 2011 年 09 月 22 日 由 renfengbin 分享到:GMAIL邮箱         Hotmail邮箱 delicious digg Auto ...

  2. 依赖注入框架Autofac学习笔记

    Autofac是一个轻量级的依赖注入框架,可以在这里面下载:http://code.google.com/p/autofac/downloads/list 要想了解什么是依赖注入,可以参考下这篇博客: ...

  3. 依赖注入之Autofac使用总结

    依赖倒置?控制反转(IOC)? 依赖注入(DI)? 你是否还在被这些名词所困扰,是否看了大量理论文章后还是一知半解了? 今天我想结合实际项目,和正在迷惑中的新手朋友一起来学习和总结依赖注入Autofa ...

  4. .NET 中依赖注入组件 Autofac 的性能漫聊

    Autofac 是一款超赞的 .NET IoC 容器 ,在众多性能测评中,它也是表现最优秀的一个.它管理类之间的依赖关系, 从而使 应用在规模及复杂性增长的情况下依然可以轻易地修改.它的实现方式是将常 ...

  5. ASP.NET Core 依赖注入-集成 Autofac

    概述 ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖关系之间实现控制反转 (IoC) 的技术. 默认服务容器是 Microsoft.Extensions.De ...

  6. .Net Core 3.0依赖注入替换 Autofac

    今天早上,喜庆的更新VS2019,终于3.0正式版了呀~ 有小伙伴问了一句Autofac怎么接入,因为Startup.ConfigureServices不能再把返回值改成IServiceProvide ...

  7. ASP.NET Core Web 应用程序系列(三)- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)...

    在上一章中主要和大家分享了在ASP.NET Core中如何使用Autofac替换自带DI进行构造函数的批量依赖注入,本章将和大家继续分享如何使之能够同时支持属性的批量依赖注入. 约定: 1.仓储层接口 ...

  8. ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)...

    在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. P ...

  9. dotNET Core 3.X 使用 Autofac 来增强依赖注入

    在上一篇<dotNET Core 3.X 依赖注入>中简单介绍了 dotNET Core 框架本身的依赖注入功能,大部分情况下使用框架的依赖注入功能就可以满足了,在一些特殊场景下,我们就需 ...

最新文章

  1. Linux意外之rpm的删除与恢复
  2. 基于android的语音识别
  3. VR规格表出来啦!Rift,Vive,PSVR,Acer和HP
  4. 卡罗林斯卡学院(Karolinska Institute)
  5. oracle使用存储过程做铺底数据
  6. 将Mac OS X从Snow Leopard升级到Mountain Lion
  7. oracle 中的几天后,几年后
  8. [ubuntu] tmux 窗口不是全屏
  9. 超级简单:如何列出目录下所有文件的超链接
  10. java改变人物动作_Java创建对象的动作分析
  11. 《矩阵分析》代码Ⅱ——gauss消元法,列主元素法,总体主元素法matlab实现
  12. 安卓系统的电视机_天猫魔盒强刷机教程,把天猫魔盒刷成安卓系统教程?
  13. swift项目:微博
  14. CSS3动画(动画已丢,看原文)
  15. 从两幅图像的匹配点计算焦距f
  16. ngx.var与ngx.ctx的区别
  17. 7.Android常用第三方支付
  18. 诚意干货:如何神不知鬼不觉破解你家路由器 | 硬创公开课
  19. MySQL 学习笔记——基础 DQL 查询语言
  20. 实验3 手写字体识别【机器学习】

热门文章

  1. python怎么爬取电影海报_Python 爬虫“王者”:豆瓣海报爬取
  2. ISO20000有什么作用?
  3. 魔兽世界 助手 无法连接服务器,魔兽世界军团再临助手无法登录解决办法分享...
  4. vmware linux 3d加速,Ubuntu虚拟机安装Virtualbox增强功能启用3D加速
  5. Python 与 数学 【1】
  6. delphi选择打印机:Delphi中获取打印机设备
  7. rn如何测试数据请求时间_宝妈ispn/rn双证励志锦囊
  8. 利用arduino UNO配合Orangepi 4B实现制作机器人
  9. 2030,保时捷全面抛弃燃油车
  10. 2021年代理记账收费标准费用及流程