A Look at ASP.NET 5: Part 2 - Startup


Posted by Shawn Wildermuth on Mar 01, 2015 on 20:16PM

dipswitchIn this second post in my six-part series on ASP.NET 5, we’ll take a look at how your ASP.NET 5 applications will be configured upon startup. The startup in this new version of ASP.NET 5 is very different, but hopefully is clearer and easier to debug. At least that’s my impression so far.

If you haven’t read the prior topics, it would probably be helpful to start with the earlier articles. You can see a list of the links to the articles below:

Part 1: Getting Started

Part 2: Startup (this post)

Part 3: Using Entity Framework 7

Part 4: ASP.NET MVC 6

Part 5: The API

Part 6: Web Tooling with VS2015 (coming soon)

Startup

In prior versions of ASP.NET, global.asax (and global.asa before then) was used to fire events on different startup times so you could configure the application. I always found this a little confusing. In ASP.NET 5, the startup.cs is the place all of this happens instead. Let’s walkthrough the boilerplate code so we can see what happens.

First, in the constructor, the configuration information is read like so:

  public class Startup
  {
    public IConfiguration Configuration { get; set; }

    public Startup(IHostingEnvironment env)
    {
      // Setup configuration sources.
      Configuration = new Configuration()
          .AddJsonFile("config.json")
          .AddEnvironmentVariables();

    }

 

The IConfiguration object that is created contains all the configuration information from both the config.json and the environment. In fact, this new configuration information replaces the web.config from the prior ASP.NET versions. Web.config was where all the old configuration was stored. AppSettings were commonly used, but it didn’t support any structure, just strings. You could create your own handlers for complex scenarios but there were a lot of ceremonial pieces to making the work and adding a new section required to bloat the web.config. You could could use machine and app-level configuration too, but rarely did people do this. Just just threw the kitchen sink in their web.config. it worked, was easy but not very flexible.

In ASP.NET 5 that changes. The IConfiguration and the Configuration class are central here. Instead of having a centralized configuration space,

var config = new Configuration()
    .AddJsonFile("config.json")
    .AddIniFile("system.ini")
    .AddCommandLine(args)
    .AddEnvironmentVariables();

The fluent syntax allows you to add configuration information from a variety of sources. It is extensible so you can add other types of configuration sources. This pattern means that configuration options are all merged into a single configuration source (though of course you could. The boilerplate combines both the config.json and the environment variables.

Once the constructor fires and reads the configuration, there are two methods that are executed in order to set up the runtime environment. The first method is called ConfigureServices. The purpose of this method is to setup dependency injection. The IServiceCollection interface is responsible for learning about any services that will be supplied to the running application. Out of the box, ASP.NET 5 includes it’s own implementation, though I expect other containers (e.g. Ninject) to supply their own implementations near RTM. You do not have to use Microsoft’s dependency injection, but at least one exists by default which I like.

// This method gets called by the runtime.
public void ConfigureServices(IServiceCollection services)
{
  // Add EF services to the services container.
  services.AddEntityFramework(Configuration)  
      .AddSqlServer()
      .AddDbContext<MyCountriesContext>();

  // Add Identity services to the services container.
  services.AddIdentity<ApplicationUser, IdentityRole>(Configuration)
      .AddEntityFrameworkStores<MyCountriesContext>();

  // Add MVC services to the services container.
  services.AddMvc();

  // Add other services
  services.AddScoped<IMyCountriesRepository, MyCountriesRepository>();
  services.AddScoped<IEmailer, Emailer>();

}

What is happening in this code is that Entity Framework, Identity and MVC are all adding themselves to the DI container. At the bottom, I have my own services that I am injecting into the container so I can use them (in this case a Repository and a service that can email items for me). Some amount of configuration can happen here too, like setting up the connection string for the EF context and adding configuration for MVC (both of which we will explain in later blog posts in detail).

But at this point, all the code is doing is making these services accessible in the dependency injection container. That’s all.

The second method that is executed is called Configure. Unlike prior versions, the startup assumes no runtime frameworks. So you have to tell ASP.NET 5 what frameworks you’ll use including MVC, Entity Framework, Identity, etc. So this method is used to opt into these frameworks like so:

// Configure is called after ConfigureServices is called.
public void Configure(IApplicationBuilder app, 
                      IHostingEnvironment env, 
                      ILoggerFactory loggerfactory)
{
  // Configure the HTTP request pipeline.
  // Add the console logger.
  loggerfactory.AddConsole();

  // Add the following to the request pipeline only in development environment.
  if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
  {
    app.UseBrowserLink();
    app.UseErrorPage(ErrorPageOptions.ShowAll);
    app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
  }
  else
  {
    // Add Error handling middleware which catches all application specific errors and
    // send the request to the following path or controller action.
    app.UseErrorHandler("/Home/Error");
  }

  // Add static files to the request pipeline.
  app.UseStaticFiles();

  // Add cookie-based authentication to the request pipeline.
  app.UseIdentity();

  // Add MVC to the request pipeline.
  app.UseMvc(routes =>
  {
    routes.MapRoute(
          name: "default",
          template: "{controller}/{action}/{id?}",
          defaults: new { controller = "Home", action = "Index" });
  });

}

The Configure method simply takes the IApplicationBuilder object (which is supplied) and allows us to opt-into these services. See how inside the test for the “Development” environment, that we’re adding browser link, full error details and showing all database errors.

Further down you can see that we’re opting into using the delivery of static files, using identity, and using ASP.NET MVC (including the definition of the default route). By understanding how this startup happens, you’ll be able to trace the configuration and setup of your web application.

Next time, we’ll start with the actual changes to the runtime code. We’ll start with Entity Framework and see how the new version of that framework is shaping up.




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.24628.01
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86