c# 高级编程 20章444页 【依赖注入】【服务的生命周期】

352 阅读3分钟

服务的生命周期,示例代码:

    public class ServiceA : IServiceA, IDisposable
    {
        private readonly int _n;
        public ServiceA(INumberService numberService)
        {
            _n = numberService.GetNumber();
            Console.WriteLine($"ctor {nameof(ServiceA)}, {_n}");
        }

        public void A() => Console.WriteLine($"{nameof(A)}, {_n}");
        public void Dispose() => Console.WriteLine($"disposing {nameof(ServiceA)}, {_n}");
    }
    
    public class ServiceB : IServiceB, IDisposable
    {
        private readonly int _n;
        public ServiceB(INumberService numberService)
        {
            _n = numberService.GetNumber();
            Console.WriteLine($"ctor {nameof(ServiceB)}, {_n}");
        }

        public void B() => Console.WriteLine($"{nameof(B)}, {_n}");
        public void Dispose() => Console.WriteLine($"disposing {nameof(ServiceB)}, {_n}");
    }
    
    public class ServiceC : IServiceC, IDisposable
    {
        private bool _isDisposed = false;
        private readonly int _n;
        public ServiceC(INumberService numberService)
        {
            _n = numberService.GetNumber();
            Console.WriteLine($"ctor {nameof(ServiceC)}, {_n}");
        }

        public void C()
        {
            if (_isDisposed)
                throw new ObjectDisposedException("ServiceC");

            Console.WriteLine($"{nameof(C)}, {_n}");
        }
        public void Dispose()
        {
            Console.WriteLine($"disposing {nameof(ServiceC)}, {_n}");
            _isDisposed = true;
        }
    }
    
    public class NumberService : INumberService
    {
        private int _number = 0;
        public int GetNumber() => Interlocked.Increment(ref _number);
    }


    public class ControllerX : IDisposable
    {
        private readonly IServiceA _serviceA;
        private readonly IServiceB _serviceB;
        private readonly int _n;
        private int _countm = 0;
        public ControllerX(IServiceA serviceA, IServiceB serviceB, INumberService numberService)
        {
            _n = numberService.GetNumber();
            Console.WriteLine($"ctor {nameof(ControllerX)}, {_n}");
            _serviceA = serviceA;
            _serviceB = serviceB;
        }

        public void M()
        {
            Console.WriteLine($"invoked {nameof(M)} for the {++_countm}. time");
            _serviceA.A();
            _serviceB.B();
        }

        public void Dispose() => Console.WriteLine($"disposing {nameof(ControllerX)}, {_n}");
    }
    public interface IServiceA
    {
        void A();
    }
    
    public interface IServiceB
    {
        void B();
    }
    
    public interface IServiceC
    {
        void C();
    }
    
    public interface INumberService
    {
        int GetNumber();
    }

调用SingletonAndTransient()的输出

    class Program 
    {
        static void Main()
        {
            SingletonAndTransient();
        }

        private static void SingletonAndTransient()
        {
            Console.WriteLine(nameof(SingletonAndTransient));

            ServiceProvider RegisterServices()
            {
                IServiceCollection services = new ServiceCollection();
                services.AddSingleton<IServiceA, ServiceA>();
                services.AddTransient<IServiceB, ServiceB>();
                // services.AddSingleton<ControllerX>();
                services.Add(new ServiceDescriptor(typeof(ControllerX), typeof(ControllerX), ServiceLifetime.Transient));
                services.AddSingleton<INumberService, NumberService>();
                return services.BuildServiceProvider();
            }

            using (ServiceProvider container = RegisterServices())
            {
                Console.WriteLine($"requesting {nameof(ControllerX)}");

                ControllerX x = container.GetRequiredService<ControllerX>();
                x.M();
                x.M();

                Console.WriteLine($"requesting {nameof(ControllerX)}");

                ControllerX x2 = container.GetRequiredService<ControllerX>();
                x2.M();

                Console.WriteLine();
            }
        }
    }
SingletonAndTransient
requesting ControllerX
ctor ServiceA, 1
ctor ServiceB, 2
ctor ControllerX, 3
invoked M for the 1. time
A, 1
B, 2
invoked M for the 2. time
A, 1
B, 2
requesting ControllerX
ctor ServiceB, 4
ctor ControllerX, 5
invoked M for the 1. time
A, 1
B, 4

disposing ControllerX, 5
disposing ServiceB, 4
disposing ControllerX, 3
disposing ServiceB, 2
disposing ServiceA, 1

UsingScoped()的输出

    class Program 
    {
        static void Main()
        {
            UsingScoped();
        }

       
        private static void UsingScoped()
        {
            Console.WriteLine(nameof(UsingScoped));

            ServiceProvider RegisterServices()
            {
                var services = new ServiceCollection();
                services.AddSingleton<INumberService, NumberService>();
                services.AddScoped<IServiceA, ServiceA>();
                services.AddSingleton<IServiceB, ServiceB>();
                services.AddTransient<IServiceC, ServiceC>();
                return services.BuildServiceProvider();
            }

            using (ServiceProvider container = RegisterServices())
            {
                using (IServiceScope scope1 = container.CreateScope())
                {
                    IServiceA a1 = scope1.ServiceProvider.GetService<IServiceA>();
                    a1.A();
                    IServiceA a2 = scope1.ServiceProvider.GetService<IServiceA>();
                    a2.A();
                    IServiceB b1 = scope1.ServiceProvider.GetService<IServiceB>();
                    b1.B();
                    IServiceC c1 = scope1.ServiceProvider.GetService<IServiceC>();
                    c1.C();
                    IServiceC c2 = scope1.ServiceProvider.GetService<IServiceC>();
                    c2.C();
                }

                Console.WriteLine("end of scope1");

                using (IServiceScope scope2 = container.CreateScope())
                {
                    IServiceA a3 = scope2.ServiceProvider.GetService<IServiceA>();
                    a3.A();
                    IServiceB b2 = scope2.ServiceProvider.GetService<IServiceB>();
                    b2.B();
                    IServiceC c3 = scope2.ServiceProvider.GetService<IServiceC>();
                    c3.C();
                }
                Console.WriteLine("end of scope2");
                Console.WriteLine();
            }
        }        
    }
UsingScoped
ctor ServiceA, 1
A, 1
A, 1
ctor ServiceB, 2
B, 2
ctor ServiceC, 3
C, 3
ctor ServiceC, 4
C, 4
disposing ServiceC, 4
disposing ServiceC, 3
disposing ServiceA, 1
end of scope1
ctor ServiceA, 5
A, 5
B, 2
ctor ServiceC, 6
C, 6
disposing ServiceC, 6
disposing ServiceA, 5
end of scope2

disposing ServiceB, 2

CustomFactories()的输出

    class Program 
    {
        static void Main()
        {
            CustomFactories();
        }

        private static void CustomFactories()
        {
            Console.WriteLine(nameof(CustomFactories));

            IServiceB CreateServiceBFactory(IServiceProvider provider) =>
                new ServiceB(provider.GetService<INumberService>());

            ServiceProvider RegisterServices()
            {
                var numberService = new NumberService();

                var services = new ServiceCollection();
                services.AddSingleton<INumberService>(numberService);  // add existing

                services.AddTransient<IServiceB>(CreateServiceBFactory);  // use a factory
                services.AddSingleton<IServiceA, ServiceA>();
                return services.BuildServiceProvider();
            }

            using (ServiceProvider container = RegisterServices())
            {
                IServiceA a1 = container.GetService<IServiceA>();
                IServiceA a2 = container.GetService<IServiceA>();
                IServiceB b1 = container.GetService<IServiceB>();
                IServiceB b2 = container.GetService<IServiceB>();
            }
            Console.WriteLine();
        }
    }
CustomFactories
ctor ServiceA, 1
ctor ServiceB, 2
ctor ServiceB, 3
disposing ServiceB, 3
disposing ServiceB, 2
disposing ServiceA, 1

服务的生命周期:

  • 单例:总是返回相同实例
  • 瞬态:每次注入服务时都会返回一个新对象。每次从容器中请求服务时,都会创建一个新实例
  • Scoped: 总是从相同的作用域返回相同的实例,不同的作用域返回不同的实例

注册服务:

  • AddSingleton和AddTransient都是ServiceCollection的扩展方法,更易于注册服务。还有一个更原始一点的Add方法,Add方法用起来更麻烦一点,并且是只有用IServiceCollection时才可以看到,用ServiceCollection的时候是看不到Add方法的
services.Add(new ServiceDescriptor(typeof(ControllerX), typeof(ControllerX), ServiceLifetime.Transient));

对于Scoped:

  • ASP.NET Core Web应用程序中,默认作用域是一个HTTP web request。当从容器中请求一个Scoped服务的时候,如果这个请求是来自同一个HTTP Web request,则返回相同的实例。反之,返回不同的实例。这允许在HTTP Web request中轻松共享状态
  • 对非ASP.NET Core web应用程序,需要自己创建作用域

服务的释放:

  • 在作用域末尾,transient和scoped服务会被自动释放,但是singleton不会被释放
  • 释放作用域时,释放transient服务和scoped服务。释放根提供程序时,释放singleton服务
  • 不需要在服务上调用Dispose来释放它们。只要实现了IDisposable接口,容器会自动调用Dispose方法
  • .NET Core 2.0 按照创建的相反顺序来释放服务

其他实例化方法:

  • 创建自定义工厂
IServiceB CreateServiceBFactory(IServiceProvider provider) =>
                new ServiceB(provider.GetService<INumberService>());
                
services.AddTransient<IServiceB>(CreateServiceBFactory); 
  • 将现有实例传给容器
var numberService = new NumberService();

services.AddSingleton<INumberService>(numberService);