[转]ASP.NET Core 3.1系列(2)——.NET Core中内置的IoC容器


原文链接:ASP.NET Core 3.1系列(2)——.NET Core中内置的IoC容器

1、前言#

作为.NET Core中最为重要的概念,依赖注入和控制反转可谓是无处不在,因此微软也为我们提供一个内置的IoC容器,下面就来介绍一下它的使用方法。

2、内置的IoC容器#

创建一个控制台程序,引入如下两个组件,版本选择3.1.23,如下图所示:

1、Microsoft.Extensions.DependencyInjection
2、Microsoft.Extensions.DependencyInjection.Abstractions

image

还是使用之前的例子,这次我们使用.NET Core内置的IoC容器来实现,代码如下所示:

using Microsoft.Extensions.DependencyInjection;
using System;

namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            // IServiceCollection负责注册
            IServiceCollection services = new ServiceCollection();
            services.AddTransient<IDb, SqlServer>();
            services.AddTransient<DbManager>();

            // IServiceProvider负责提供实例
            IServiceProvider provider = services.BuildServiceProvider();
            DbManager manager = provider.GetService<DbManager>();
            manager.Read();
        }
    }

    public interface IDb
    {
        void Read();
    }

    public class SqlServer : IDb
    {
        public void Read()
        {
            Console.WriteLine("SqlServer读取数据");
        }
    }

    public class DbManager
    {
        private IDb db;

        public DbManager(IDb db)
        {
            this.db = db;
        }

        public void Read()
        {
            db.Read();
        }
    }
}

.NET Core中负责依赖注入和控制反转的核心组件有两个:IServiceCollectionIServiceProvider。其中,IServiceCollection负责注册,IServiceProvider负责提供实例。与Autofac类似,.NET Core内置的IoC容器也是按照创建容器、注册接口和类、获取对象的流程实现控制反转。

3、容器内实例的生命周期#

在注册接口和类时,IServiceCollection提供了三种注册方法,如下所示:

1、services.AddTransient<IDb, SqlServer>();  // 瞬时生命周期
2、services.AddScoped<IDb, SqlServer>();     // 域生命周期
3、services.AddSingleton<IDb, SqlServer>();  // 全局单例生命周期

3.1、瞬时生命周期——AddTransient#

如果使用AddTransient方法注册,IServiceProvider每次都会通过GetService方法创建一个新的实例,代码如下所示:

using Microsoft.Extensions.DependencyInjection;
using System;

namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            services.AddTransient<IDb, SqlServer>();
            IServiceProvider provider = services.BuildServiceProvider();

            IDb db_1 = provider.GetService<IDb>();
            IDb db_2 = provider.GetService<IDb>();
            IDb db_3 = provider.GetService<IDb>();

            Console.WriteLine(db_1.GetHashCode());
            Console.WriteLine(db_2.GetHashCode());
            Console.WriteLine(db_3.GetHashCode());
        }
    }

    public interface IDb
    {
        void Read();
    }

    public class SqlServer : IDb
    {
        public void Read()
        {
            Console.WriteLine("SqlServer读取数据");
        }
    }
}

运行结果如下所示:

32854180
27252167
43942917

3.2、域生命周期——AddScoped#

如果使用AddScoped方法注册, 在同一个域(Scope)内,IServiceProvider每次都会通过GetService方法调用同一个实例,可以理解为在局部实现了单例模式,代码如下所示:

using Microsoft.Extensions.DependencyInjection;
using System;

namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            services.AddScoped<IDb, SqlServer>();
            IServiceProvider provider = services.BuildServiceProvider();

            IDb db_1 = provider.GetService<IDb>();
            IDb db_2 = provider.GetService<IDb>();
            IDb db_3 = provider.GetService<IDb>();

            Console.WriteLine(db_1.GetHashCode());
            Console.WriteLine(db_2.GetHashCode());
            Console.WriteLine(db_3.GetHashCode());
        }
    }

    public interface IDb
    {
        void Read();
    }

    public class SqlServer : IDb
    {
        public void Read()
        {
            Console.WriteLine("SqlServer读取数据");
        }
    }
}

运行结果如下所示:

32854180
32854180
32854180

3.3、全局单例生命周期——AddSingleton#

如果使用AddSingleton方法注册, 在整个应用程序生命周期内,IServiceProvider只会创建一个实例,代码如下所示:

using Microsoft.Extensions.DependencyInjection;
using System;

namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            services.AddSingleton<IDb, SqlServer>();
            IServiceProvider provider = services.BuildServiceProvider();

            IDb db_1 = provider.GetService<IDb>();
            IDb db_2 = provider.GetService<IDb>();
            IDb db_3 = provider.GetService<IDb>();

            Console.WriteLine(db_1.GetHashCode());
            Console.WriteLine(db_2.GetHashCode());
            Console.WriteLine(db_3.GetHashCode());
        }
    }

    public interface IDb
    {
        void Read();
    }

    public class SqlServer : IDb
    {
        public void Read()
        {
            Console.WriteLine("SqlServer读取数据");
        }
    }
}

运行结果如下所示:

32854180
32854180
32854180

3.4、AddScoped和AddSingleton的区别#

通过上面的代码可以发现,域生命周期和全局单例生命周期的输出结果是一样的,那么它们之间有什么区别?看下面一段代码:

using Microsoft.Extensions.DependencyInjection;
using System;

namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            services.AddScoped<IDb, SqlServer>();
            IServiceProvider provider = services.BuildServiceProvider();

            using (var scope_1 = provider.CreateScope())
            {
                var p = scope_1.ServiceProvider;

                IDb db_1 = p.GetService<IDb>();
                IDb db_2 = p.GetService<IDb>();
                IDb db_3 = p.GetService<IDb>();

                Console.WriteLine("------------------scope_1------------------");
                Console.WriteLine(db_1.GetHashCode());
                Console.WriteLine(db_2.GetHashCode());
                Console.WriteLine(db_3.GetHashCode());
            }

            using (var scope_2 = provider.CreateScope())
            {
                var p = scope_2.ServiceProvider;

                IDb db_1 = p.GetService<IDb>();
                IDb db_2 = p.GetService<IDb>();
                IDb db_3 = p.GetService<IDb>();

                Console.WriteLine("------------------scope_2------------------");
                Console.WriteLine(db_1.GetHashCode());
                Console.WriteLine(db_2.GetHashCode());
                Console.WriteLine(db_3.GetHashCode());
            }
        }
    }

    public interface IDb
    {
        void Read();
    }

    public class SqlServer : IDb
    {
        public void Read()
        {
            Console.WriteLine("SqlServer读取数据");
        }
    }
}

如果采用AddScoped注册,那么在同一个域中,每个实例的哈希值都是一样的,但在不同域中,它们又是不一样的,其输出结果如下所示:

------------------scope_1------------------
32854180
32854180
32854180
------------------scope_2------------------
27252167
27252167
27252167

如果采用AddSingleton注册,那么不同域中的实例的哈希值都是一样的,因为AddSingleton方法注册的实例在全局是唯一的,其输出结果如下所示:

------------------scope_1------------------
32854180
32854180
32854180
------------------scope_2------------------
32854180
32854180
32854180

4、结语#

本文主要介绍了.NET Core中内置的IoC容器的使用方法。回想一下三层架构的年代,一些公共类,诸如CacheHelper、CookieHelper,我们都可以在.NET Core内置的IoC容器中把它们注册为全局单例生命周期,而服务层的相关类则可以注册为域生命周期。

posted @   二次元攻城狮  阅读(160)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
  1. 1 烟花易冷 小柔Channel
  2. 2 红颜如霜 江壹纯
  3. 3 不谓侠 小桃Channel
  4. 4 小小恋歌 新坦结衣
  5. 5 神预言 袁娅维TIARAY
神预言 - 袁娅维TIARAY
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.
点击右上角即可分享
微信分享提示
主题色彩