重新整理 .net core 实践篇————依赖注入应用之援军[四]
前言
介绍第三方依赖注入框架Autofac,看看为我们解决什么问题。
下面介绍4个点:
命名注册
属性注册
aop 注入
子容器命名
正文
为什么我们需要使用第三方框架?第三方框架为我们做了什么?第三方框架扩展了哪一个部分?
这里主要介绍一下Autofac。
Autofac 主要是替换了我们ServiceProviderFactory 这个东西。
public interface IServiceProviderFactory<IContainerBuilder>
我们使用的时候这样用:
Host.CreateDefaultBuilder(args).UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
看下UseServiceProviderFactory 源码:
private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
{
_serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
return this;
}
所以说是替换了我们的serviceProviderFactory。
原理篇<<从新整理1400篇>>介绍了这个东西,这里再简要介绍一下。
_serviceProviderFactory 通过代理模式下进行的,也就是一个适配过程,那么我们直接看适配器。
其实适配器有一个小的隐藏信息哈。比如说_serviceProviderFactory的命名上看,翻译过来就是serviceProvider的构建工厂,也就是为我们提供serviceProvider的生产工厂。
直接看ServiceFactoryAdapter的源码。
internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter
{
private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
private readonly Func<HostBuilderContext> _contextResolver;
private Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;
public ServiceFactoryAdapter(
IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
{
IServiceProviderFactory<TContainerBuilder> serviceProviderFactory1 = serviceProviderFactory;
if (serviceProviderFactory1 == null)
throw new ArgumentNullException(nameof (serviceProviderFactory));
this._serviceProviderFactory = serviceProviderFactory1;
}
public ServiceFactoryAdapter(
Func<HostBuilderContext> contextResolver,
Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
{
Func<HostBuilderContext> func1 = contextResolver;
if (func1 == null)
throw new ArgumentNullException(nameof (contextResolver));
this._contextResolver = func1;
Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> func2 = factoryResolver;
if (func2 == null)
throw new ArgumentNullException(nameof (factoryResolver));
this._factoryResolver = func2;
}
public object CreateBuilder(IServiceCollection services)
{
if (this._serviceProviderFactory == null)
{
this._serviceProviderFactory = this._factoryResolver(this._contextResolver());
if (this._serviceProviderFactory == null)
throw new InvalidOperationException("The resolver returned a null IServiceProviderFactory");
}
return (object) this._serviceProviderFactory.CreateBuilder(services);
}
public IServiceProvider CreateServiceProvider(object containerBuilder)
{
if (this._serviceProviderFactory == null)
throw new InvalidOperationException("CreateBuilder must be called before CreateServiceProvider");
return this._serviceProviderFactory.CreateServiceProvider((TContainerBuilder) containerBuilder);
}
}
看这个CreateServiceProvider方法,和猜想的一致。再来看下HostBuilder调用情况。
private void CreateServiceProvider()
{
var services = new ServiceCollection();
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
#pragma warning restore CS0618 // Type or member is obsolete
services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
services.AddSingleton(_hostBuilderContext);
// register configuration as factory to make it dispose with the service provider
services.AddSingleton(_ => _appConfiguration);
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
#pragma warning restore CS0618 // Type or member is obsolete
services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
services.AddSingleton<IHostLifetime, ConsoleLifetime>();
services.AddSingleton<IHost, Internal.Host>();
services.AddOptions();
services.AddLogging();
foreach (var configureServicesAction in _configureServicesActions)
{
configureServicesAction(_hostBuilderContext, services);
}
var containerBuilder = _serviceProviderFactory.CreateBuilder(services);
foreach (var containerAction in _configureContainerActions)
{
containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
}
_appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
if (_appServices == null)
{
throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
}
// resolve configuration explicitly once to mark it as resolved within the
// service provider, ensuring it will be properly disposed with the provider
_ = _appServices.GetService<IConfiguration>();
}
的却如此。完全证实了这个猜想。
那么serviceProver 用来做什么的呢?
1: internal class ServiceProvider : IServiceProvider, IDisposable
2: {
3: public ServiceProvider Root { get; private set; }
4: public ServiceTable ServiceTable { get; private set; }
5: public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { get; private set; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
6: public IList<IDisposable> TransientDisposableServices { get; private set; } = new List<IDisposable>();
7: public ConcurrentDictionary<IService, object> ResolvedServices { get; private set; } = new ConcurrentDictionary<IService, object>();
8:
9: public ServiceProvider(IServiceCollection services)
10: {
11: this.Root = this;
12: this.ServiceTable = new ServiceTable(services);
13: }
14:
15: public object GetService(Type serviceType)
16: {
17: Func<ServiceProvider, object> serviceAccessor;
18: if (this.RealizedServices.TryGetValue(serviceType, out serviceAccessor))
19: {
20: return serviceAccessor(this);
21: }
22:
23: IServiceCallSite serviceCallSite = this.GetServiceCallSite(serviceType, new HashSet<Type>());
24: if (null != serviceCallSite)
25: {
26: var providerExpression = Expression.Parameter(typeof(ServiceProvider), "provider");
27: this.RealizedServices[serviceType] = Expression.Lambda<Func<ServiceProvider, object>>(serviceCallSite.Build(providerExpression), providerExpression).Compile();
28: return serviceCallSite.Invoke(this);
29: }
30:
31: this.RealizedServices[serviceType] = _ => null;
32: return null;
33: }
34:
35: public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)
36: {
37: try
38: {
39: if (callSiteChain.Contains(serviceType))
40: {
41: throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type '{0}'", serviceType.FullName);
42: }
43: callSiteChain.Add(serviceType);
44:
45: ServiceEntry serviceEntry;
46: if (this.ServiceTable.ServieEntries.TryGetValue(serviceType,
47: out serviceEntry))
48: {
49: return serviceEntry.Last.CreateCallSite(this, callSiteChain);
50: }
51:
52: //省略其他代码
53:
54: return null;
55: }
56: finally
57: {
58: callSiteChain.Remove(serviceType);
59: }
60: }
61:
62: public void Dispose()
63: {
64: Array.ForEach(this.TransientDisposableServices.ToArray(), _ => _.Dispose());
65: Array.ForEach(this.ResolvedServices.Values.ToArray(), _ => (_ as IDisposable)?.Dispose());
66: this.TransientDisposableServices.Clear();
67: this.ResolvedServices.Clear();
68: }
69: //其他成员
70: }
看到GetService 是否特别的眼熟?这个就是通过我们的注册信息来产生不同的对象的。
那么Autofac的作用就是替换了我们的ServiceProvider。也就是替换了,往容器注入的方式了。
那么他给我们扩展了什么功能,后面就不介绍具体源码了,应为是实践篇,主要解释用法。
命名注册
为什么有命名注册的方式呢?有什么痛点呢?
比如说:
services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
return new MySingletonService();
});
那么可以肯定一点的是,这时候通过IMySingletonService 可以获取第一个,也可以获取全部。
但是假如我要获取指定的一个呢?那么要给他们加上标记,但是.net core 自带的并没有并没有。
这时候使用autofac。
在startUp 中加入:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<TestService>().As<ITestService>();
builder.RegisterType<TestService>().Named<ITestService>("service2");
}
然后在startUp 的Configure 中加入:
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
Console.WriteLine(testServiceNameDefault.GetHashCode());
var testServiceName2= this.AutofacContainer.ResolveNamed<ITestService>("service2");
Console.WriteLine(testServiceName2.GetHashCode());

发现他们的hashcode并不一致,故而获取了不同对象。
属性注册
把这个TestService 修改一下:
public interface ITestService
{
public void ShowAttributeState();
}
public class TestService:ITestService,IDisposable
{
public AttributeService Attribute { get; set; }
public void ShowAttributeState()
{
Console.WriteLine($"attribute is null?{(Attribute == null ? "true":"false")}");
}
public void Dispose()
{
Console.WriteLine($"DisposableTestService Disposed:{this.GetHashCode()}");
}
}
AttributeService 如下:
public class AttributeService
{
}
AttributeService 的注册信息:
然后添加:
services.AddTransient<AttributeService>();
builder.RegisterType<TestService>().As<ITestService>();
注:这里我特意用services 来注册AttributeService 是为了证明autofac 兼容了.net core 原生的注册信息,证明前面的替换serviceProvider 的推导过程,这样我们就可以在我们的老项目中直接使用。
获取信息:
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
testServiceNameDefault.ShowAttributeState();

TestService的注册信息修改一下:
builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired();

设置自动注入属性,这样就可以了。如果对象属性有注册信息的话,会帮我们自动填充。
aop 注入
aop 如果没怎么看的话,可以简单理解可以理解为拦截器。
一般我们看到属性的方式来写加入拦截器:
[Attribuite]
public void method(){
}
这样是显式注入拦截器,那么autofac 为我们隐式注入。
安装一下Castle.core。然后我们写一个拦截器。
public class TestInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Invocation before Method:{invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"Invocation after Method:{invocation.Method.Name}");
}
}
然后我们注册的时候注入拦截器。
安装一下:Autofac.Extras.DynamicProxy
写入注册信息:
builder.RegisterType<TestInterceptor>();
builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired().InterceptedBy(typeof(TestInterceptor)).EnableInterfaceInterceptors();
然后调用:
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
testServiceNameDefault.ShowAttributeState();
看下结果:

给容器命名
builder.RegisterType<TestService>().As<ITestService>().InstancePerMatchingLifetimeScope("selfScope");
然后调用:
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
using (var myScope = AutofacContainer.BeginLifetimeScope())
{
var obj = myScope.Resolve<ITestService>();
Console.WriteLine(obj==null?"true":"false");
}
这里会报错myScope.Resolve();,因为获取不到,指定了指定容器selfScope。
要这样写:
using (var myScope = AutofacContainer.BeginLifetimeScope("selfScope"))
{
var obj = myScope.Resolve<ITestService>();
Console.WriteLine(obj==null?"true":"false");
}
这么写好像是不能体现出这个容器命名有什么作用。
画一张概念图:

上述是原先.net core 的一个隔离思路。
如果给容器命名的话,相当于每个scope可以继续套娃,起一个隔离作用。

代码演示:
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
using (var myScope = AutofacContainer.BeginLifetimeScope("selfScope"))
{
var obj = myScope.Resolve<ITestService>();
using (var myChildScope = myScope.BeginLifetimeScope())
{
var obj1 = myChildScope.Resolve<ITestService>();
Console.WriteLine(obj.GetHashCode());
Console.WriteLine(obj1.GetHashCode());
}
}

这种隔离机制做项目的时候就能体现出来,因为可能有几个服务共同用到了某个类,这样解决管理困难问题。
结
上述只是个人整理,如有问题,望请指出,谢谢。
下一节:配置之盟约。
重新整理 .net core 实践篇————依赖注入应用之援军[四]的更多相关文章
- 重新整理 .net core 实践篇————依赖注入应用[二]
前言 这里介绍一下.net core的依赖注入框架,其中其代码原理在我的另一个整理<<重新整理 1400篇>>中已经写了,故而专门整理应用这一块. 以下只是个人整理,如有问题, ...
- 重新整理 .net core 实践篇————依赖注入应用之生命法则[三]
前言 该章演示依赖注入中,对象的释放行为. 紧接上文表示,演示: services.AddSingleton<IMySingletonService, MySingletonService> ...
- 重新整理 .net core 实践篇——— 权限中间件源码阅读[四十六]
前言 前面介绍了认证中间件,下面看一下授权中间件. 正文 app.UseAuthorization(); 授权中间件是这个,前面我们提及到认证中间件并不会让整个中间件停止. 认证中间件就两个作用,我们 ...
- 重新整理 .net core 实践篇————polly失败重试[三十四]
前言 简单整理一下polly 重试. 正文 在开发程序中一般都有一个重试帮助类,那么polly同样有这个功能. polly 组件包: polly 功能包 polly.Extensions.Http 专 ...
- 重新整理 .net core 实践篇—————3种配置验证[十四]
前言 简单整理一些配置的验证. 正文 配置的验证大概分为3类: 直接注册验证函数 实现IValidteOptions 使用Microsoft.Extensions.Options.DataAnnota ...
- 重新整理 .net core 实践篇————配置应用[一]
前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...
- ASP.NET Core之依赖注入
本文翻译自:http://www.tutorialsteacher.com/core/dependency-injection-in-aspnet-core ASP.NET Core支持依赖注入,依赖 ...
- 【半小时大话.net依赖注入】(下)详解AutoFac+实战Mvc、Api以及.NET Core的依赖注入
系列目录 上|理论基础+实战控制台程序实现AutoFac注入 下|详解AutoFac+实战Mvc.Api以及.NET Core的依赖注入 前言 本来计划是五篇文章的,每章发个半小时随便翻翻就能懂,但是 ...
- 几十行代码实现ASP.NET Core自动依赖注入
在开发.NET Core web服务的时候,我们习惯使用自带的依赖注入容器来进行注入. 于是就会经常进行一个很频繁的的重复动作:定义一个接口->写实现类->注入 有时候会忘了写Add这一步 ...
随机推荐
- Python学习笔记-PuLP库(3)线性规划实例
本节以一个实际数学建模案例,讲解 PuLP 求解线性规划问题的建模与编程. 1.问题描述 某厂生产甲乙两种饮料,每百箱甲饮料需用原料6千克.工人10名,获利10万元:每百箱乙饮料需用原料5千克.工人2 ...
- 1.7.6- 浏览器审查HTML标签元素
或者F12
- 缓冲区溢出分析第09课:MS06-040漏洞研究——深入挖掘
前言 经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用.在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否 ...
- hdu4911 简单树状数组
题意: 给你一串数字,然后给你最多进行k次交换(只能交换相邻的)问交换后的最小逆序数是多少. 思路: 首先要知道的一个就是给你一个序列,每次只能交换相邻的位置,把他交换成一个递增序 ...
- POJ2431贪心(最少加油次数)
题意: 给一个终点,然后给你一个卡车距离终点的距离,还有其他个加油站距离终点的距离,然后每走一个单位距离要花费一个单位油,卡车的邮箱是无限大的,而每个加油站的油量是有限的,整个路径是一个 ...
- CVE-2013-0077:Microsoft DirectShow quartz.dll m2p 文件堆溢出漏洞简单分析
0x01 前言 2012 年 10 月 5 日,exploit-db 漏洞公布站点上发布了 QQplayer.exe 3.7.892 m2p quartz.dll Heap Pointer OverW ...
- 逆向 string.h 函数库 strlen、memchr、strcat 函数
strlen 函数 主要功能:返回字符串的长度 C/C++ 实现: #include <iostream> #include <stdio.h> #include <st ...
- Andrew Ng机器学习算法入门(三):线性回归算法
线性回归 线性回归,就是能够用一个直线较为精确地描述数据之间的关系.这样当出现新的数据的时候,就能够预测出一个简单的值. 线性回归中最常见的就是房价的问题.一直存在很多房屋面积和房价的数据,如下图所示 ...
- gitlab + php自动部署
功能简介 本地往服务器推送代码之后,触发web钩子,服务器拉取刚刚推送的代码 步骤 1.在gitlab后台配置钩子 项目->编辑项目->Web钩子->新增钩子 2.在服务器端为www ...
- JPEG头部解析
6.3 JPEG格式 6.3.1简介 微处理机中的存放顺序有正序(big endian)和逆序(little endian)之分.正序存放就是高字节存放在前低字节在后,而逆序存放就是低字 ...