Ejemplo n.º 1
0
        public GenericGameHostBuilder(IHostBuilder builder)
        {
            _builder = builder;

            _config = new ConfigurationBuilder()
                      .AddEnvironmentVariables(prefix: "GAMENETCORE_")
                      .Build();

            _builder.ConfigureHostConfiguration(config =>
            {
                config.AddConfiguration(_config);

                // We do this super early but still late enough that we can process the configuration
                // wired up by calls to UseSetting
                ExecuteHostingStartups();
            });

            // IHostingStartup needs to be executed before any direct methods on the builder
            // so register these callbacks first
            _builder.ConfigureAppConfiguration((context, configurationBuilder) =>
            {
                if (_hostingStartupGameHostBuilder != null)
                {
                    var gamehostContext = GetGameHostBuilderContext(context);
                    _hostingStartupGameHostBuilder.ConfigureAppConfiguration(gamehostContext, configurationBuilder);
                }
            });

            _builder.ConfigureServices((context, services) =>
            {
                if (_hostingStartupGameHostBuilder != null)
                {
                    var gamehostContext = GetGameHostBuilderContext(context);
                    _hostingStartupGameHostBuilder.ConfigureServices(gamehostContext, services);
                }
            });

            _builder.ConfigureServices((context, services) =>
            {
                var gamehostContext = GetGameHostBuilderContext(context);
                var gameHostOptions = (GameHostOptions)context.Properties[typeof(GameHostOptions)];

                // Add the IHostingEnvironment and IApplicationLifetime from Microsoft.AspNetCore.Hosting
                services.AddSingleton(gamehostContext.HostingEnvironment);
#pragma warning disable CS0618 // Type or member is obsolete
                services.AddSingleton((GameNetCore.Hosting.IHostingEnvironment)gamehostContext.HostingEnvironment);
                services.AddSingleton <IApplicationLifetime, GenericGameHostApplicationLifetime>();
#pragma warning restore CS0618 // Type or member is obsolete

                services.Configure <GenericGameHostServiceOptions>(options =>
                {
                    // Set the options
                    options.GameHostOptions = gameHostOptions;
                    // Store and forward any startup errors
                    options.HostingStartupExceptions = _hostingStartupErrors;
                });

                services.AddHostedService <GenericGameHostService>();

                // REVIEW: This is bad since we don't own this type. Anybody could add one of these and it would mess things up
                // We need to flow this differently
                var listener = new DiagnosticListener("Contoso.GameNetCore");
                services.TryAddSingleton <DiagnosticListener>(listener);
                services.TryAddSingleton <DiagnosticSource>(listener);

                services.TryAddSingleton <IProtoContextFactory, DefaultProtoContextFactory>();
                services.TryAddScoped <IMiddlewareFactory, MiddlewareFactory>();
                services.TryAddSingleton <IApplicationBuilderFactory, ApplicationBuilderFactory>();

                // Support UseStartup(assemblyName)
                if (!string.IsNullOrEmpty(gameHostOptions.StartupAssembly))
                {
                    try
                    {
                        var startupType = StartupLoader.FindStartupType(gameHostOptions.StartupAssembly, gamehostContext.HostingEnvironment.EnvironmentName);
                        UseStartup(startupType, context, services);
                    }
                    catch (Exception ex) when(gameHostOptions.CaptureStartupErrors)
                    {
                        var capture = ExceptionDispatchInfo.Capture(ex);

                        services.Configure <GenericGameHostServiceOptions>(options =>
                        {
                            options.ConfigureApplication = app =>
                            {
                                // Throw if there was any errors initializing startup
                                capture.Throw();
                            };
                        });
                    }
                }
            });
        }
Ejemplo n.º 2
0
        void UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
        {
            var gameHostBuilderContext = GetGameHostBuilderContext(context);
            var gameHostOptions        = (GameHostOptions)context.Properties[typeof(GameHostOptions)];

            ExceptionDispatchInfo startupError = null;
            object           instance          = null;
            ConfigureBuilder configureBuilder  = null;

            try
            {
                // We cannot support methods that return IServiceProvider as that is terminal and we need ConfigureServices to compose
                if (typeof(IStartup).IsAssignableFrom(startupType))
                {
                    throw new NotSupportedException($"{typeof(IStartup)} isn't supported");
                }
                if (StartupLoader.HasConfigureServicesIServiceProviderDelegate(startupType, context.HostingEnvironment.EnvironmentName))
                {
                    throw new NotSupportedException($"ConfigureServices returning an {typeof(IServiceProvider)} isn't supported.");
                }

                instance = ActivatorUtilities.CreateInstance(new HostServiceProvider(gameHostBuilderContext), startupType);
                context.Properties[_startupKey] = instance;

                // Startup.ConfigureServices
                var configureServicesBuilder = StartupLoader.FindConfigureServicesDelegate(startupType, context.HostingEnvironment.EnvironmentName);
                var configureServices        = configureServicesBuilder.Build(instance);

                configureServices(services);

                // REVIEW: We're doing this in the callback so that we have access to the hosting environment
                // Startup.ConfigureContainer
                var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName);
                if (configureContainerBuilder.MethodInfo != null)
                {
                    var containerType = configureContainerBuilder.GetContainerType();
                    // Store the builder in the property bag
                    _builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder;

                    var actionType = typeof(Action <,>).MakeGenericType(typeof(HostBuilderContext), containerType);

                    // Get the private ConfigureContainer method on this type then close over the container type
                    var configureCallback = GetType().GetMethod(nameof(ConfigureContainer), BindingFlags.NonPublic | BindingFlags.Instance)
                                            .MakeGenericMethod(containerType)
                                            .CreateDelegate(actionType, this);

                    // _builder.ConfigureContainer<T>(ConfigureContainer);
                    typeof(IHostBuilder).GetMethods().First(m => m.Name == nameof(IHostBuilder.ConfigureContainer))
                    .MakeGenericMethod(containerType)
                    .InvokeWithoutWrappingExceptions(_builder, new object[] { configureCallback });
                }

                // Resolve Configure after calling ConfigureServices and ConfigureContainer
                configureBuilder = StartupLoader.FindConfigureDelegate(startupType, context.HostingEnvironment.EnvironmentName);
            }
            catch (Exception ex) when(gameHostOptions.CaptureStartupErrors)
            {
                startupError = ExceptionDispatchInfo.Capture(ex);
            }

            // Startup.Configure
            services.Configure <GenericGameHostServiceOptions>(options =>
            {
                options.ConfigureApplication = app =>
                {
                    // Throw if there was any errors initializing startup
                    startupError?.Throw();

                    // Execute Startup.Configure
                    if (instance != null && configureBuilder != null)
                    {
                        configureBuilder.Build(instance)(app);
                    }
                };
            });
        }