.Net Core-依赖注入IoC

一、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

   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: }

以上借助他人的代码片段,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)。

时间: 2024-10-07 13:44:49

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

大话DI依赖注入+IOC控制反转(二) 之 浅析.Net Core中的DI与IOC

原文:大话DI依赖注入+IOC控制反转(二) 之 浅析.Net Core中的DI与IOC   转发时请注明原创作者及地址,否则追究责任.原创:alunchen 在上一篇文章中,我们聊了很多关于定义的方面,比较孤燥,下面我们结合.Net Core聊一下依赖注入&控制反转. 三种对象生命周期 关于.Net Core中的容器,有三种对象的生命周期,这个从网上搜索也有大堆的资料.为了循序渐进,我们这里介绍一下. Transient 称为短暂,意思是需要使用时就创建一个新的对象.从容易层面讲,当从容器取出

NET Core依赖注入解读&amp;使用Autofac替代实现

NET Core依赖注入解读&使用Autofac替代实现 ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autofac 3.2 创建容器并注册依赖 4. 参考链接 1. 前言 关于IoC模式(控制反转)和DI技术(依赖注入),我们已经见过很多的探讨,这里就不再赘述了.比如说必看的Martin Fowler<IoC 容器和 Dependency Inj

从壹开始前后端分离【 .NET Core2.0 Api + Vue 3.0 + AOP + 分布式】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

代码已上传Github,文末有地址 说接上文,上回说到了<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之八 || API项目整体搭建 6.3 异步泛型+依赖注入初探>,后来的标题中,我把仓储两个字给去掉了,因为好像大家对这个模式很有不同的看法,嗯~可能还是我学艺不精,没有说到其中的好处,现在在学DDD领域驱动设计相关资料,有了好的灵感再给大家分享吧. 到目前为止我们的项目已经有了基本的雏形,后端其实已经可以搭建自己的接口列表了,框架已

ASP.NET Core 依赖注入(DI)

原文:ASP.NET Core 依赖注入(DI) ASP.NET Core的底层设计支持和使用依赖注入.ASP.NET Core 应用程序可以利用内置的框架服务将服务注入到启动类的方法中,并且应用程序服务也可以配置注入.由ASP.NET Core 提供的默认服务容器提供了最小功能集,并不是取代其他容器. 1.浅谈依赖注入 依赖注入(Dependency injection,DI)是一种实现对象和依赖者之间松耦合的技术,将类用来执行其操作的这些对象以注入的方式提供给该类,而不是直接实例化依赖项或者

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

ASP.NET Core 依赖注入 ASP.NET Core从框架层对依赖注入提供支持.也就是说,如果你不了解依赖注入,将很难适应 ASP.NET Core的开发模式.本文将介绍依赖注入的基本概念,并结合代码演示如何在 ASP.NET Core中使用依赖注入. 什么是依赖注入? 百度百科对于依赖注入的介绍: 控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.其中最常见的方式叫做依赖注入(Dependency I

android使用篇(四) 注解依赖注入IOC实现绑定控件

在android使用篇(三) MVC模式中提到一个问题: 1) 视图层(View):一般採用XML文件进行界面的描写叙述,使用的时候能够很方便的引入,可是用xml编写了,又须要在Acitvity声明而且实例化,有点麻烦,考虑是否能做一个类似注解实现匹配,或者写一个类获取xml的各个节点然后自己主动进行封装,当然,这仅仅是个想法,以后再实现. 今天最终把这个想法实现了,使用依赖注入IOC注解实现对activity中控件的实例化. 先普及一下java的反射机制和注解机制的知识: 下面引用大神的两篇文

【Spring】Spring依赖注入IOC的设值注入setter

其实标题中如此高大上的名词,只是一个问题,关于在Spring的applicationContext.xml中的如下语句是什么意思? <property name="aService" ref="aService"/> 这类语句在SSH的配置中会大量存在,因为Spring所谓的核心机制就是Spring依赖注入IOC.下面举一个例子说明这个问题: 先贴上目录结构: 在Eclipse中新建一个Java工程,不是JavaWeb,在这个Java配置好Spring3

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

在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇文章假设你已经基本熟悉依赖注入和ASP.NET Core.如果不是,则先阅读文章: 在ASP.NET Core中使用依赖注入 基础 构造函数注入 构造函数注入常用于在服务构建上定义和获取服务依赖.例如: 1 public class ProductService 2 { 3 private read

asp.net core 依赖注入

依赖注入入门 全面理解 ASP.NET Core 依赖注入 参考https://www.cnblogs.com/tcjiaan/p/8732848.html 如何在StartUp中的ConfigureServices方法里直接调用刚刚添加好的注册? // redis注入 services.AddSingleton<IRedisConnection>(k => { return new RedisConnection(6, Configuration["RedisConnecti

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

在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用中使用依赖注入的一些经验和建议,并且将会讨论这些原则背后的动机是什么: (1)有效地设计服务及其依赖关系. (2)防止多线程问题. (3)防止内存泄漏. (4)防止潜在的错误. 在讨论该话题之前,了解什么是服务是生命周期至关重要,当组件通过依赖注入请求另一个组件时,它接收的实例是否对该组件实例是唯一