7

Here's the skeleton of a standard ASP.NET Core application:

var config = new ConfigurationBuilder()
    .AddCommandLine(args)
    .AddEnvironmentVariables(prefix: "ASPNETCORE_")
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

host.Run();

In this piece the ASP.NET Core apparatus instantiates an instance of Startup.cs class

.UseStartup<Startup>()

My query is how can I get hold (reference) of this already instantiated instance of Startup object that I can plug into my Library/Framework.

Context is to setup some Uber level framework and get a reference of this junction (Startup.cs) where all the requests are getting initiated.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
user2727195
  • 7,122
  • 17
  • 70
  • 118

1 Answers1

10

If your Startup implements IStartup interface, getting reference to it is easy:

var host = new WebHostBuilder()
.UseConfiguration(config)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();

var startup = host.Services.GetService(typeof(IStartup)); // or from any other part of code using IServiceProvider.

However, asp.net core does not require your startup class to implement this interface. If it does not - it will use adapter pattern and adapt your Startup class to IStartup interface. You will still have an instance of IStartup, but it will not be your Startup class. Instead it will be an instance of ConventionBasedStartup. Asp.net core will explore methods of your startup class, find Configure and ConfigureServices methods and will pass them to ConventionBasedStartup which will adapt them to IStartup interface. In this case, it's not possible to retrieve instance of your startup class without heavy reflection, because it's not actually stored in any field (even in private) of ConventionBasedStartup and is only reachable through delegate references.

Long story short - if you want to get instance of your Startup class - make it implement IStartup interface.

Update about how to implement IStartup interface:

public class Startup : IStartup
{
    public Startup(IHostingEnvironment env)
    {
        // constructor as usual
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void Configure(IApplicationBuilder app) {            
        app.UseMvc();
        // resolve services from container
        var env = (IHostingEnvironment) app.ApplicationServices.GetService(typeof(IHostingEnvironment));
        var logger = (ILoggerFactory)app.ApplicationServices.GetService(typeof(ILoggerFactory));
        logger.AddConsole(Configuration.GetSection("Logging"));
        logger.AddDebug();
        // etc
    }        

    public IServiceProvider ConfigureServices(IServiceCollection services) {
        services.AddMvc();
        // etc
        // return provider
        return services.BuildServiceProvider();
    }
}
Evk
  • 98,527
  • 8
  • 141
  • 191
  • Thanks, this seems promising, I've added `IStartup` as an interface to the `Startup` class, it's complaining for `ConfigureServices` return type, question is what should I return within `ConfigureServices` to match requirements of `IStartup` interface, I'm new to .net. – user2727195 Feb 05 '17 at 14:27
  • Thanks, visual studio code is complaining for few dependencies, for instance `ConfigurationBuilder` and `IConfigurationRoot`. I have created a basic bare bone simplest web app using generator `yo aspnet`. Want a simplest implementation at `Startup.cs` level since heavy lifting will be done in my framework. Also I don't want to plug into `mvc` of `asp.net mvc` since I've my own custom implementation. At the very simplest level I want the Startup object just to `delegate` the incoming requests to my framework. – user2727195 Feb 05 '17 at 16:27
  • Note that my example just shows how to implement IStartup interface. IConfigurationRoot, Startup constructor, UseMvc etc are provided just as placeholders to show you how you can do that. It is in no way necessary. Just implement Configure and ConfigureServices with provided signatures however you see fit. – Evk Feb 05 '17 at 16:38
  • Thanks for a comprehensive reply... got it. I'm testing, – user2727195 Feb 05 '17 at 16:42
  • almost there, it works fine, using new method `Configure(IApplicationBuilder app), the old overloaded method with `IHostingEnvironment` and `ILoggerFactory` params are not in use and that's fine except that I might need to know hosting environment in my uber framework, is there a way to retrieve it there? – user2727195 Feb 05 '17 at 19:41
  • Just read my example once again, I show there how to retrieve IHostingEnvironment and ILoggerFactory in Configure method, exactly what you are asking. – Evk Feb 05 '17 at 19:42
  • Thanks. Appreciate. – user2727195 Feb 05 '17 at 20:37
  • Hi, can you please also look into this, http://stackoverflow.com/questions/42062316/intercepting-and-delegating-requests-to-a-custom-object-library – user2727195 Feb 06 '17 at 07:23
  • 2
    I tried this solution, but it cause a new Startup instance to be created. Any suggestion(s)? – Tom Dec 03 '17 at 14:19