一、Ioc

IoC全称Inverse of Control,控制反转。

类库和框架的不同之处在于,类库是实现某种单一功能的API,框架是针对一个任务把这些单一功能串联起来形成一个完整的流程,这个流程在一个引擎驱动下被执行。

IoC的总体设计是要把在应用程序的流程控制转移到框架中,实现对流程的复用,这 符合软件设计的基本原则-重用性。

IoC不是一种设计模式,它是一种设计原则。并且很多设计模式都遵循了这种原则。比如:模板模式、工厂模式、抽象工厂模式。

二、DI

DI全称Dependency Indection,依赖注入。它是IoC模式的一种。

我们写的对象要完成某个功能可能要依赖于另外一个对象,比如我们要添加一个人员,往往会在逻辑层调用数据操作层来实现添加到数据库的操作。DI会在程序初始化的时候,把数据操作层的接口和实现接口的类关联起来,那么在逻辑层我们只要声明要调用的具体数据操作层的接口,调用接口的方法,这样就实现了逻辑层和数据操作层的解耦。

所谓依赖注入可以理解为一种针对依赖的字段或者属性的一种自动初始化方式。

举例说明,当我们通过VS2015创建了一个.Net Core项目,并且带有个人身份验证Identity,具体如何创建在此不再细说。

我们看到在Startup中,有个方法

// This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)

        {

            // Add framework services.

            services.AddDbContext<ApplicationDbContext>(options =>

                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()

                .AddEntityFrameworkStores<ApplicationDbContext>()

                .AddDefaultTokenProviders();

            services.AddMvc();

            // Add application services.

            services.AddTransient<IEmailSender, AuthMessageSender>();

            services.AddTransient<ISmsSender, AuthMessageSender>();

        }

Controller中:

[Authorize]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ILogger _logger; public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<AccountController>();
}
}

我们看到,我们并没有实例化AccountController中的字段,只是在构造函数把相关联的字段当做参数传进来,系统自动就会实例化。这个实例化的过程就是DI帮助我们完成的。

DI注入方式有3种:构造器注入、属性注入、方法注入。

DI容器就是一个服务的提供者。当需要某个服务的时候,只要从DI容器中获取就可以。

三、.NET 中的DI

ServiceProvider对象是DI容器的核心对象,他在Microsoft.Extensions.DependencyInjection.dll程序集的同名命名空间下。是一个程序集内部类。这个类的主要任务就是根据服务类型提供服务对象。

与这个类相关联的类有:ServiceCallSite、Service、ServiceEntry和ServiceTable。他们都有相应的接口。

1.ServiceCallSite

服务的最终提供者就是这个对象,这个对象继承自接口IServiceCallSite。

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq.Expressions; internal interface IServiceCallSite
{
Expression Build(Expression provider);
object Invoke(ServiceProvider provider);
}
}

当需要一个服务实例的时候,会去对应的ServiceCallSite中调用Invoke方法,返回需要的实例。同时,还会调用Invoke方法返回表达式,并会缓存起来,等到下次获取相同实例的时候直接获取。

2.Service

当我们获取服务对象实例的时候是根据ServiceCollection提供的。ServiceCollection和我们连接非常紧密,就是我们在Startup中添加的服务于实现的关联。

ServiceCollection中保存的就是ServiceDescriptor,每个ServiceDescriptor都会被转化成Service,继承自接口IService。

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic; internal interface IService
{
IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain); ServiceLifetime Lifetime { get; } IService Next { get; set; }
}
}

我们看到,这个接口中有CreateCallSite得到一个ServiceCallSite实例。每个Service都是以链表的形式存在,Next表示链表的下一个节点,这个链表就是具有相同的服务类型组成。

3.ServiceEntry

表示上面说的链表。

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
using System;
using System.Runtime.CompilerServices; internal class ServiceEntry
{
private object _sync = new object(); public ServiceEntry(IService service)
{
this.First = service;
this.Last = service;
} public void Add(IService service)
{
object obj2 = this._sync;
lock (obj2)
{
this.Last.Next = service;
this.Last = service;
}
} public IService First { get; private set; } public IService Last { get; private set; }
}
}

4.ServiceTable

多个ServiceEntry组成一个ServiceTable。

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices; internal class ServiceTable
{
private readonly Dictionary<Type, List<IGenericService>> _genericServices = new Dictionary<Type, List<IGenericService>>();
private readonly ConcurrentDictionary<Type, Func<ServiceProvider, object>> _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
private readonly Dictionary<Type, ServiceEntry> _services = new Dictionary<Type, ServiceEntry>();
private readonly object _sync = new object(); public ServiceTable(IEnumerable<ServiceDescriptor> descriptors)
{
foreach (ServiceDescriptor descriptor in descriptors)
{
if (IntrospectionExtensions.GetTypeInfo(descriptor.ServiceType).IsGenericTypeDefinition)
{
TypeInfo info = (descriptor.ImplementationType == null) ? null : IntrospectionExtensions.GetTypeInfo(descriptor.ImplementationType);
if ((info == null) || !info.IsGenericTypeDefinition)
{
throw new ArgumentException(Resources.FormatOpenGenericServiceRequiresOpenGenericImplementation(descriptor.ServiceType), "descriptors");
}
if (info.IsAbstract || info.IsInterface)
{
throw new ArgumentException(Resources.FormatTypeCannotBeActivated(descriptor.ImplementationType, descriptor.ServiceType));
}
this.Add(descriptor.ServiceType, new GenericService(descriptor));
}
else if (descriptor.ImplementationInstance != null)
{
this.Add(descriptor.ServiceType, new InstanceService(descriptor));
}
else if (descriptor.ImplementationFactory != null)
{
this.Add(descriptor.ServiceType, new FactoryService(descriptor));
}
else
{
TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(descriptor.ImplementationType);
if ((typeInfo.IsGenericTypeDefinition || typeInfo.IsAbstract) || typeInfo.IsInterface)
{
throw new ArgumentException(Resources.FormatTypeCannotBeActivated(descriptor.ImplementationType, descriptor.ServiceType));
}
this.Add(descriptor.ServiceType, new Service(descriptor));
}
}
} public void Add(Type serviceType, IGenericService genericService)
{
object obj2 = this._sync;
lock (obj2)
{
List<IGenericService> list;
if (!this._genericServices.TryGetValue(serviceType, ref list))
{
list = new List<IGenericService>();
this._genericServices.set_Item(serviceType, list);
}
list.Add(genericService);
}
} public void Add(Type serviceType, IService service)
{
object obj2 = this._sync;
lock (obj2)
{
ServiceEntry entry;
if (this._services.TryGetValue(serviceType, ref entry))
{
entry.Add(service);
}
else
{
this._services.set_Item(serviceType, new ServiceEntry(service));
}
}
} public bool TryGetEntry(Type serviceType, out ServiceEntry entry)
{
object obj2 = this._sync;
lock (obj2)
{
if (this._services.TryGetValue(serviceType, ref entry))
{
return true;
}
if (IntrospectionExtensions.GetTypeInfo(serviceType).IsGenericType)
{
List<IGenericService> list;
Type genericTypeDefinition = serviceType.GetGenericTypeDefinition();
if (this._genericServices.TryGetValue(genericTypeDefinition, ref list))
{
using (List<IGenericService>.Enumerator enumerator = list.GetEnumerator())
{
while (enumerator.MoveNext())
{
IService service = enumerator.get_Current().GetService(serviceType);
if (service != null)
{
this.Add(serviceType, service);
}
}
}
return this._services.TryGetValue(serviceType, ref entry);
}
}
}
return false;
} public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices
{
get
{
return this._realizedServices;
}
}
}
}

主要的方法就是在ServiceTable构造函数中,根据传过来的ServiceDescriptor列表,其实就是我们在Startup中注册的服务与具体实现转换过来的。

在这个构造函数中会先根据ServiceType转换成ServiceEntry列表,再添加到Dictionary<Type, ServiceEntry> _services 中,也就是最终得到的是_services类型。

5.ServiceProvider

   :   internal class ServiceProvider : IServiceProvider, IDisposable

   : {

   :     public ServiceProvider Root { get; private set; }

   :     public ServiceTable ServiceTable { get; private set; }

   :     public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { get; private set; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();

   :     public IList<IDisposable> TransientDisposableServices { get; private set; } = new List<IDisposable>();

   :     public ConcurrentDictionary<IService, object> ResolvedServices { get; private set; } = new ConcurrentDictionary<IService, object>();

   :     

   :     public ServiceProvider(IServiceCollection services)

  :     {

  :         this.Root         = this;

  :         this.ServiceTable     = new ServiceTable(services);

  :     }

  :  

  :     public object GetService(Type serviceType)

  :     {

  :         Func<ServiceProvider, object> serviceAccessor;

  :         if (this.RealizedServices.TryGetValue(serviceType, out serviceAccessor))

  :         {

  :             return serviceAccessor(this);

  :         }

  :  

  :         IServiceCallSite serviceCallSite = this.GetServiceCallSite(serviceType, new HashSet<Type>());

  :         if (null != serviceCallSite)

  :         {

  :             var providerExpression = Expression.Parameter(typeof(ServiceProvider), "provider");

  :             this.RealizedServices[serviceType] = Expression.Lambda<Func<ServiceProvider, object>>(serviceCallSite.Build(providerExpression), providerExpression).Compile();

  :             return serviceCallSite.Invoke(this);

  :         }

  :  

  :         this.RealizedServices[serviceType] = _ => null;

  :         return null;

  :     }

  :  

  :     public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)

  :     {

  :             try

  :             {

  :                 if (callSiteChain.Contains(serviceType))

  :                 {

  :                     throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type '{0}'", serviceType.FullName);

  :                 }

  :                 callSiteChain.Add(serviceType);

  :  

  :                 ServiceEntry serviceEntry;

  :                 if (this.ServiceTable.ServieEntries.TryGetValue(serviceType, 

  :                     out serviceEntry))

  :                 {

  :                     return serviceEntry.Last.CreateCallSite(this, callSiteChain);

  :                 }

  :  

  :                 //省略其他代码

  :  

  :                 return null;

  :             }

  :             finally

  :             {

  :                 callSiteChain.Remove(serviceType);

  :             }

  :     }    

  :  

  :     public void Dispose()

  :     {

  :         Array.ForEach(this.TransientDisposableServices.ToArray(), _ => _.Dispose());

  :         Array.ForEach(this.ResolvedServices.Values.ToArray(), _ => (_ as IDisposable)?.Dispose());

  :         this.TransientDisposableServices.Clear();

  :         this.ResolvedServices.Clear();

  :     }

  :     //其他成员

  : }

以上借助他人的代码片段,ServiceProvider中主要有几个属性,root指向自己;RealizedServices 就是在我们讲解ServiceCallSite时说道,最后得到的Service会被生成委托以便下次调用,这个委托就存在这个属性中。

这个类最主要的方法就是GetService,主要逻辑就是从RealizedServices 获取当前服务的实例,如果有,直接返回,如果没有,会从ServiceTable中找到对应的ServiceEntry,如果没有返回null,如果有,调用ServiceEntry所在列表最后一个Service的CreateServiceCallSite方法创建一个ServiceCallSite对象(这一点说明了如果针对同一个服务类型注册了多个ServiceDescriptor,在提供单个服务的时候总是使用最后一个 ServiceDescriptor)。

综上,.net core中的DI容器的主要对象就是ServiceProvider、ServiceCallSite、Service、ServiceEntry和ServiceTable。主要的流程控制都放在ServiceProvider类中,这个类有一个ServiceTable(就是ServiceType和ServiceEntry的对应列表)。ServiceEntry就是一个链表,链接了当前ServiceType的所有的实例(不过得到的实例总是以最后一个为准),实例的类型都是Service类型。Service主要就是获取ServiceCallSite对象,这个对象就是封装了所有的获取具体服务实例的逻辑,主要通过Invoke得到实例,再调用Build生成表达式委托,存在ServiceProvider中。

ServiceProvider主要有一个方法GetService获取服务实例。主要逻辑就是从RealizedServices 获取当前服务的实例,如果有,直接返回,如果没有,会从ServiceTable中找到对应的ServiceEntry,如果没有返回null,如果有,调用ServiceEntry所在列表最后一个Service的CreateServiceCallSite方法创建一个ServiceCallSite对象(这一点说明了如果针对同一个服务类型注册了多个ServiceDescriptor,在提供单个服务的时候总是使用最后一个 ServiceDescriptor)。

过后思考:

1. ServiceCallSite:获取我们要的最终的服务实例并缓存起来以备下次调用。

2.Service:获取ServiceCallSite,由我们注册的服务转换而来,链表形式存在,整个链表表示相同实例类型的实例。

3.ServiceEntry:对链表Service的封装,有First、Last表示链表的第一个和最后一个,还有个Add方法。

4.ServiceTable:主要对象就是Dictionary<Type,ServiceEntry> _service,表示服务类型和服务实例链表的对应关系。

5.ServiceProvider:整个DI功能的控制者,主要方法就是GetService,根据类型获取实例。

ASP.Net Core-依赖注入IoC的更多相关文章

  1. # ASP.NET Core依赖注入解读&使用Autofac替代实现

    标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...

  2. ASP.NET Core依赖注入最佳实践,提示&技巧

    分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...

  3. ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】

    ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...

  4. ASP.NET Core 依赖注入基本用法

    ASP.NET Core 依赖注入 ASP.NET Core从框架层对依赖注入提供支持.也就是说,如果你不了解依赖注入,将很难适应 ASP.NET Core的开发模式.本文将介绍依赖注入的基本概念,并 ...

  5. 实现BUG自动检测 - ASP.NET Core依赖注入

    我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...

  6. [译]ASP.NET Core依赖注入深入讨论

    原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...

  7. asp.net core 依赖注入几种常见情况

    先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...

  8. ASP.NET Core依赖注入——依赖注入最佳实践

    在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...

  9. 自动化CodeReview - ASP.NET Core依赖注入

    自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...

  10. ASP.NET Core 依赖注入最佳实践——提示与技巧

    在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...

随机推荐

  1. 编程之美 3.1 字符串移位包含问 复杂度(O(N*K)

    分享关于编程之美3.1自己编写的代码,很简单. s2.沿着s1匹配(循环匹配,利用%Length技巧),匹配上,返回true. //BOP3.1 char src[] = "AABBCD&q ...

  2. Linux下安装Wine运行windows程序

    资料 首页 https://www.winehq.org/ 安装 https://www.winehq.org/download/ 教程 https://www.winehq.org/document ...

  3. Ubuntu安装已经下载好的文件包

    默认的文件下载都在 ~/Downloads 文件夹里面. 按 ctrl+alt+t 打开命令. 1.解压下载好的文件包,如: tar -xvf Sublime\ Text\ 2.0.2.tar.bz2 ...

  4. c# 产生随机字符串,包括大小写字母和数字

    #region MyRegion //產生密碼 protected static string GetPwd() { return CreateRandomNum123(2) + CreateRand ...

  5. vim 退出保留显示的内容

    /*************************************************************************** * vim 退出保留显示的内容 * 声明: * ...

  6. Android 数据传输之MessagePack使用

    介绍过什么是MessagePack之后,就进行Android与MessagePack的使用. 在MessagePack的官网上介绍MessagePack与Java结合使用的都是使用Maven作为JAR ...

  7. 使用appium做安卓手机web自动化测试 真机demo

    一:appium相关环境搭建过程略. 二:连接真机: 1.手机(andriod 4.2.2)连接电脑,打开USB调试模式. 2.运行cmd 输入 adb devices -l 查看UDID,如图: 3 ...

  8. 430flash的操作

    大概印象:430的flash好像有点像arm的flash,只不过是arm的flash要比430的大很多,而且430的flash不同于E2PROOM,这一点需要值得注意 MSP430flash的基本特点 ...

  9. class0513(html)

    精通DIV+CSS Meta 1.div span 2.三种样式表 内联样式(行内样式) 嵌入样式 外部样式 就近原则 3.常见样式 复合样式background border css单位 % px ...

  10. codeforce 606C - Sorting Railway Cars

    题意:给你一串数,没个数只能往前提到首位,或则往后放末尾.问最少步骤使操作后的序列成上升序列. 思路:最长连续子序列. #include<iostream> #include<std ...