C# 依赖注入 生命周期

在前面的章节中我们有看到了 AddScoped AddTransient 和 AddSingleton 这三种方式来添加依赖注入的配置.

AddSingleton 单例模式

单例模式是比较好理解的。不管哪一个类需要这个依赖项,这个对象在整个系统中只会有一个实例。

AddScoped 模式

这个模式是比较特殊的,就是同一个scope下面只会创建一次。 假设我们有如下三个类

    public class Worker
    {
        private readonly IMessageWriter _messageWriter;

        public Worker(IMessageWriter messageWriter)
        {
            _messageWriter = messageWriter;
            Console.WriteLine("worker:" + _messageWriter.GetHashCode());
        }
    }

    public class Worker2
    {
        private readonly IMessageWriter _messageWriter;

        public Worker2(IMessageWriter messageWriter, InnerWorker innerWorker)
        {
            _messageWriter = messageWriter;
            Console.WriteLine("worker2:" + _messageWriter.GetHashCode());
        }
    }

    public class InnerWorker
    {
        private readonly IMessageWriter _messageWriter;

        public InnerWorker(IMessageWriter messageWriter)
        {
            _messageWriter = messageWriter;
            Console.WriteLine("InnerWorker:" + _messageWriter.GetHashCode());
        }
    }


public static class DIConfig
{
    public static IServiceProvider ConfigureServices()
    {
        var serviceCollection = new ServiceCollection();
        var configuration = new ConfigurationBuilder()
        .SetBasePath(AppContext.BaseDirectory)
        .AddJsonFile("appsettings.json", true)
        .AddEnvironmentVariables()// 把环境变量也放到 Configuraiton当中
        .Build();

        serviceCollection.AddSingleton((IConfiguration)configuration);

        serviceCollection.AddLogging(loggingBuilder =>
        {
            loggingBuilder.AddConfiguration(configuration.GetSection("Logging")); //配置logging的一些东西
            // 下面的这行需要 Microsoft.Extensions.Logging.Console
            loggingBuilder.AddConsole(); //加多个 每一个Ilooger下面就会有多个provider
        });

        // 注入了一个默认的ILogger
        serviceCollection.AddSingleton<Microsoft.Extensions.Logging.ILogger>((it) =>
        {
            return it.GetService<ILoggerFactory>().CreateLogger("categtory");
        });

        Register(serviceCollection, configuration);

        return serviceCollection.BuildServiceProvider();
    }

    private static void Register(IServiceCollection services, IConfiguration configuration)
    {
        //注入方式变动的地方
        services.AddScoped<IMessageWriter, MessageWriter>();
        services.AddTransient<InnerWorker>();
        services.AddTransient<Worker>();
        services.AddTransient<Worker2>();


        services.AddTransient(typeof(Lazy<>),typeof(LazyLoader<>));

        services.AddScoped(typeof(IMalemaLogger<>), typeof(MalemaLogger<>));
        services.AddSingleton(sp =>
        {
            var conn = configuration.GetConnectionString("malema");
            return new Func<MalemaDbContext>(() =>
            {
                var builder = new DbContextOptionsBuilder<MalemaDbContext>();
                builder.UseSqlServer(conn);
                return new MalemaDbContext(builder.Options);
            });
        });
    }
}

Singleton 和 Scoped 调用 Scoped

当我们用下面的方式配置时

            services.AddScoped<IMessageWriter, MessageWriter>();
            services.AddScoped<InnerWorker>();
            services.AddSingleton<Worker>();
            services.AddSingleton<Worker2>();

      static async Task Main(string[] args)
        {
            var sp = DIConfig.ConfigureServices();
            var worker = sp.GetService<Worker>();
            var worker2 = sp.GetService<Worker2>();
            var scope = sp.CreateScope();
            var scopeWorker = scope.ServiceProvider.GetService<Worker>();
            var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();
        }

会输出

worker:65204782
InnerWorker:65204782
worker2:65204782
Scope分割线--------------

注: Asp.net core 的场景下,Singleton 所依赖的接口如果被注册为Scoped时会报错。 Lifetime: Singleton ImplementationType: : Cannot consume scoped service

Scoped 调用Scoped

当我们用下面的方式配置时

            services.AddScoped<IMessageWriter, MessageWriter>();
            services.AddScoped<InnerWorker>();
            services.AddSingleton<Worker>();
            services.AddScoped<Worker2>();

            //跟前面一样没有变化
        static async Task Main(string[] args)
        {
            var sp = DIConfig.ConfigureServices();
            var worker = sp.GetService<Worker>();
            var worker2 = sp.GetService<Worker2>();
            var scope = sp.CreateScope();
            var scopeWorker = scope.ServiceProvider.GetService<Worker>();//同一个scope出来的
            var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();//同一个scope出来的
        }

会输出

worker:65204782
InnerWorker:65204782
worker2:65204782  //虽然注入的是scope并且MessageWriter也是scope 但实际上就是根的scope
Scope分割线--------------
InnerWorker:49972132 //新scoped 创建出来不一样了,但是跟worker2的是一样的
worker2:49972132  //新scoped 创建出来不一样了

备注: Asp.net Controller的每一次请求都是一个新Scope。

Transient调用Scoped

当我们用下面的方式配置时

            services.AddScoped<IMessageWriter, MessageWriter>();
            services.AddTransient<InnerWorker>();
            services.AddTransient<Worker>();
            services.AddTransient<Worker2>();
                        //跟前面一样没有变化
        static async Task Main(string[] args)
        {
            var sp = DIConfig.ConfigureServices();
            var worker = sp.GetService<Worker>();
            var worker2 = sp.GetService<Worker2>();
            var scope = sp.CreateScope();
            var scopeWorker = scope.ServiceProvider.GetService<Worker>();
            var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();
        }

会输出

worker:65204782
InnerWorker:65204782
worker2:65204782
Scope分割线--------------
worker:49972132 
InnerWorker:49972132
worker2:49972132

我们可以发现Scope的还是一样的, 虽然调用它的类是Transient。

AddTransient 有需要的时候都会创建一个新的。

当下面这种方式注入时

  services.AddTransient<IMessageWriter, MessageWriter>();
            services.AddScoped<InnerWorker>();
            services.AddSingleton<Worker>();
            services.AddScoped<Worker2>();
                        //跟前面一样没有变化
        static async Task Main(string[] args)
        {
            var sp = DIConfig.ConfigureServices();
            var worker = sp.GetService<Worker>();
            var worker2 = sp.GetService<Worker2>();
            var scope = sp.CreateScope();
            var scopeWorker = scope.ServiceProvider.GetService<Worker>();
            var scopeWorker2 = scope.ServiceProvider.GetService<Worker2>();
        }

会输出

worker:65204782
InnerWorker:49972132
worker2:47096010
Scope分割线--------------
InnerWorker:21210914
worker2:56680499

每一个都是新的。不管有没有在scope或者单例中。

最近更新的
...