ASP.NET Core - source code parsing - Program.cs (b)

ASP.NET Core - source code parsing - Program.cs (b)

Benpian already included in asp.net core Essay Series

Code analysis

Then continue to resolve the above, when CreateWebHostBuilderthe method is finished, the main function used IWebHostBuilderfor subsequent operations.

    //为什么关注这个类, 因为这里有main函数, 一般来说main函数都是程序启动的时候的启动类. 看一下这行代码:
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
  1. Objects created WebHostBuilder
  2. Let this WebHostBuilder objects build and run up a webhost

Let this WebHostBuilder objects build and run up a webhost

.Build().Run();

.Build()

Builds an Microsoft.AspNetCore.Hosting.IWebHost which hosts a web application.

WebHostBuilder look at the source code:

/// <summary>
/// Builds the required services and an <see cref="IWebHost"/> which hosts a web application.
/// </summary>
public IWebHost Build()
{
    if (_webHostBuilt)
    {
        throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
    }
    _webHostBuilt = true;

    var hostingServices = BuildCommonServices(out var hostingStartupErrors);
    var applicationServices = hostingServices.Clone();
    var hostingServiceProvider = GetProviderFromFactory(hostingServices);

    if (!_options.SuppressStatusMessages)
    {
        // Warn about deprecated environment variables
        if (Environment.GetEnvironmentVariable("Hosting:Environment") != null)
                {
                    Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
                }

        if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null)
                {
                    Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
                }

        if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null)
                {
                    Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'");
                }
    }

    AddApplicationServices(applicationServices, hostingServiceProvider);

    var host = new WebHost(
        applicationServices,
        hostingServiceProvider,
        _options,
        _config,
        hostingStartupErrors);
    try
    {
        host.Initialize();

        var logger = host.Services.GetRequiredService<ILogger<WebHost>>();

        // Warn about duplicate HostingStartupAssemblies
        foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().GroupBy(a => a, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1))
        {
            logger.LogWarning($"The assembly {assemblyName} was specified multiple times. Hosting startup assemblies should only be specified once.");
        }

        return host;
    }
    catch
    {
        // Dispose the host if there's a failure to initialize, this should clean up
        // will dispose services that were constructed until the exception was thrown
        host.Dispose();
        throw;
    }

    IServiceProvider GetProviderFromFactory(IServiceCollection collection)
    {
        var provider = collection.BuildServiceProvider();
        var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();

        if (factory != null && !(factory is DefaultServiceProviderFactory))
        {
            using (provider)
            {
                return factory.CreateServiceProvider(factory.CreateBuilder(collection));
            }
        }

        return provider;
    }
}

Well so much the way we slowly analysis, first of all look at this method BuildCommonServiceway to do what?

private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
        {
            hostingStartupErrors = null;

            _options = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name);

            if (!_options.PreventHostingStartup)
            {
                var exceptions = new List<Exception>();

                // Execute the hosting startup assemblies
                foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase))
                {
                    try
                    {
                        var assembly = Assembly.Load(new AssemblyName(assemblyName));

                        foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
                        {
                            var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
                            hostingStartup.Configure(this);
                        }
                    }
                    catch (Exception ex)
                    {
                        // Capture any errors that happen during startup
                        exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
                    }
                }

                if (exceptions.Count > 0)
                {
                    hostingStartupErrors = new AggregateException(exceptions);
                }
            }

            var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory);

            // Initialize the hosting environment
            _hostingEnvironment.Initialize(contentRootPath, _options);
            _context.HostingEnvironment = _hostingEnvironment;

            var services = new ServiceCollection();
            services.AddSingleton(_options);
            services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
            services.AddSingleton<Extensions.Hosting.IHostingEnvironment>(_hostingEnvironment);
            services.AddSingleton(_context);

            var builder = new ConfigurationBuilder()
                .SetBasePath(_hostingEnvironment.ContentRootPath)
                .AddConfiguration(_config);

            foreach (var configureAppConfiguration in _configureAppConfigurationBuilderDelegates)
            {
                configureAppConfiguration(_context, builder);
            }

            var configuration = builder.Build();
            services.AddSingleton<IConfiguration>(configuration);
            _context.Configuration = configuration;

            var listener = new DiagnosticListener("Microsoft.AspNetCore");
            services.AddSingleton<DiagnosticListener>(listener);
            services.AddSingleton<DiagnosticSource>(listener);

            services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
            services.AddTransient<IHttpContextFactory, HttpContextFactory>();
            services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
            services.AddOptions();
            services.AddLogging();

            // Conjure up a RequestServices
            services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
            services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();

            // Ensure object pooling is available everywhere.
            services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

            if (!string.IsNullOrEmpty(_options.StartupAssembly))
            {
                try
                {
                    var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);

                    if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
                    {
                        services.AddSingleton(typeof(IStartup), startupType);
                    }
                    else
                    {
                        services.AddSingleton(typeof(IStartup), sp =>
                        {
                            var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();
                            var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
                            return new ConventionBasedStartup(methods);
                        });
                    }
                }
                catch (Exception ex)
                {
                    var capture = ExceptionDispatchInfo.Capture(ex);
                    services.AddSingleton<IStartup>(_ =>
                    {
                        capture.Throw();
                        return null;
                    });
                }
            }

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

            return services;
        }

The return value is IServiceCollection, the previous article, we explained the concept of the object, showing the current configuration set in each container services, ASP.NET Core built-in dependency injection container. Since it is a container, we take a look at the configuration of the container .

This namespace is not asp.net core source code which needs to be downloaded separately, the address is Microsoft.Extensions.DependencyInjection , of course, you can also use the tool to get decompile source code.

ServiceDescriptorThe official explanation is:

Describes a service with its service type, implementation, and lifetime.

BuildCommonServiceFor the method ServiceCollectionof example some services registered service operation.

Registration of service required scope and different life cycle, corresponding to the different methods of registration, namely AddSingleton, AddScoped, AddTransient, and to achieve these namespaces registration method is Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions, Shi ServiceCollectionextensions.

Transient objects are always different; a new instance is provided to every controller and every service.
Scoped objects are the same within a request, but different across different requests.
Singleton objects are the same for every object and every request.

IWebHostBuilderIn the implementation of build, it will be a lot of service, including our own startup.cs file service IServiceCollection injected into the container when the injection service is complete, start building. WebHostProcess objects:

var host = new WebHost(
    applicationServices,
    hostingServiceProvider,
    _options,
    _config,
    hostingStartupErrors);
try
{
    host.Initialize();

    var logger = host.Services.GetRequiredService<ILogger<WebHost>>();

    // Warn about duplicate HostingStartupAssemblies
    foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().GroupBy(a => a, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1))
    {
        logger.LogWarning($"The assembly {assemblyName} was specified multiple times. Hosting startup assemblies should only be specified once.");
    }

    return host;
}
catch
{
    // Dispose the host if there's a failure to initialize, this should clean up
    // will dispose services that were constructed until the exception was thrown
    host.Dispose();
    throw;
}

Well, so far a WebHost build out.

Run()

Runs a web application and block the calling thread until host shutdown.

Man of few words said, directly on the code:

/// <summary>
/// Runs a web application and block the calling thread until host shutdown.
/// </summary>
/// <param name="host">The <see cref="IWebHost"/> to run.</param>
public static void Run(this IWebHost host)
{
    host.RunAsync().GetAwaiter().GetResult();
}

/// <summary>
/// Runs a web application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="host">The <see cref="IWebHost"/> to run.</param>
/// <param name="token">The token to trigger shutdown.</param>
public static async Task RunAsync(this IWebHost host, CancellationToken token = default)
{
    // Wait for token shutdown if it can be canceled
    if (token.CanBeCanceled)
    {
        await host.RunAsync(token, shutdownMessage: null);
        return;
    }

    // If token cannot be canceled, attach Ctrl+C and SIGTERM shutdown
    var done = new ManualResetEventSlim(false);
    using (var cts = new CancellationTokenSource())
    {
        var shutdownMessage = host.Services.GetRequiredService<WebHostOptions>().SuppressStatusMessages ? string.Empty : "Application is shutting down...";
        AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: shutdownMessage);

        try
        {
            await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
        }
        finally
        {
            done.Set();
        }
    }
}

Focus is on the following, we look inside the boot code, Serverand how to start Server:

public virtual async Task StartAsync(CancellationToken cancellationToken = default)
{
    HostingEventSource.Log.HostStart();
    _logger = _applicationServices.GetRequiredService<ILogger<WebHost>>();
    _logger.Starting();

    var application = BuildApplication();

    _applicationLifetime = _applicationServices.GetRequiredService<IApplicationLifetime>() as ApplicationLifetime;
    _hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>();
    var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticListener>();
    var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>();
    var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory);
    await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false);

    // Fire IApplicationLifetime.Started
    _applicationLifetime?.NotifyStarted();

    // Fire IHostedService.Start
    await _hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false);

    _logger.Started();

    // Log the fact that we did load hosting startup assemblies.
    if (_logger.IsEnabled(LogLevel.Debug))
            {
                foreach (var assembly in _options.GetFinalHostingStartupAssemblies())
                {
                    _logger.LogDebug("Loaded hosting startup assembly {assemblyName}", assembly);
                }
            }

    if (_hostingStartupErrors != null)
            {
                foreach (var exception in _hostingStartupErrors.InnerExceptions)
                {
                    _logger.HostingStartupAssemblyError(exception);
                }
            }
}

Guess you like

Origin www.cnblogs.com/it-dennis/p/12626020.html