谁以及如何在依赖注入中初始化接口

Who and How initialize the Interface in Dependence Injection

对于Asp.Net核心依赖注入,我知道我们将依赖注册到IServiceCollection,并使用IServiceProvider来获取实例。

我想知道注册和初始化 IServiceCollection 的代码。

对于接口注入,为什么它知道从ServiceCollection中获取实例?哪个代码实现了这个功能?

我想知道全局控制器是谁以及如何控制它?

创建ASP.NET核心项目时,会为Program.Main()生成以下代码:

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

IServiceCollection 的实例在 WebHost.CreateDefaultBuilder(args) 中创建,然后传递给 Startup.ConfigureServices(IServiceCollection services) 调用。

如果您想跟踪 ASP.NET 核心源代码中的调用链,请点击此处(包括 github 上的源代码链接):

WebHost.CreateDefaultBuilder() 调用 WebHostBuilderExtensions.UseDefaultServiceProvider() 扩展方法:

public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
    var builder = new WebHostBuilder()

        .UseIISIntegration()
        // ...
        .UseDefaultServiceProvider((context, options) =>
        {
            options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
        });

        // ...

    return builder;
}

WebHostBuilderExtensions.UseDefaultServiceProvider() 调用 WebHostBuilder.ConfigureServices() 方法:

public static IWebHostBuilder UseDefaultServiceProvider(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ServiceProviderOptions> configure)
{
    return hostBuilder.ConfigureServices((context, services) =>
    {
        var options = new ServiceProviderOptions();
        configure(context, options);
        services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(new DefaultServiceProviderFactory(options)));
    });
}

WebHostBuilder 最终创建 ServiceCollection 的实例并调用 Startup.ConfigureServices() 方法(通过存储的操作):

private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
{
    //  ...

    // Creation of `ServiceCollection` instance
    var services = new ServiceCollection();

    //  ...

    foreach (var configureServices in _configureServicesDelegates)
    {
        configureServices(_context, services);
    }

    //  ...
}

为了补充已经提供的答案,在 asp.net-core 之外,DI 框架可以单独使用,因为它是一个完全解耦的模块。

Essential .NET - Dependency Injection with .NET Core

public static void Main() {
    IServiceCollection serviceCollection = new ServiceCollection();
    ConfigureServices(serviceCollection);
    var application = new MyApplication(serviceCollection);

    // Run
    // ...
}

static private void ConfigureServices(IServiceCollection services) {
    ILoggerFactory loggerFactory = new Logging.LoggerFactory();
    services.AddInstance<ILoggerFactory>(loggerFactory);

    //...
}

唯一的区别是现在您必须自己创建集合,而不是框架在其管道中为您创建集合。

来自评论,

It bears mentioning that an ASP.NET Core app is nothing but a console app, which therefore explains why you can use IServiceColleciton in a basic console app as well, or anywhere else you like, for that matter.

这是我在 .net core 2.2 上的做法:

using System;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace ConsoleAppDependencyInjection
{
    class Program
    {
        static void Main(string[] args)
        {
            var serviceProvider = ConfigureContainer();
            var service1 = serviceProvider.GetService<Service1>();
            var service2 = serviceProvider.GetService<Service2>();

            Debug.Assert(service1 != null);
            Debug.Assert(service2 != null);
        }

        static IServiceProvider ConfigureContainer()
        {
            var services = new ServiceCollection();
            ConfigureServices(services);
            return services.BuildServiceProvider();
        }

        static void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<Service1>();
            services.AddSingleton<Service2>();
        }
    }

    public class Service1
    {
        public Service1()
        {
        }
    }

    public class Service2
    {
        Service1 service1;
        public Service2(Service1 service1)
        {
            this.service1 = service1;
        }
    }
}