CodeDigest.Com Logo
Featured:

Beginning Asp.Net Core – (Part 2) Understand Asp.Net Core Project Structure in Visual Studio 2017

Tagged as: Asp.Net Core .Net Core Posted By

My previous article Beginning Asp.Net Core - Part 1 (Understanding the Basics) explained the basics of Asp.Net Core and how Asp.Net Core is evolved from Asp.Net framework. Microsoft released Asp.Net Core 1.0 project support from Visual Studio 2015 Update 3. From then, the Asp.Net Core project structure and layout went through many changes based on the feedbacks and recently Microsoft released Visual Studio 2017 with all the new changes. The next version of Asp.Net Core version 1.1 is released with Visual Studio 2017. So, Asp.Net Core project structure that comes as part of Visual Studio 2017 is the final one and any projects with Visual Studio 2015 format has to be migrated/converted to new 2017 project format. In other words, any new Asp.Net Core project should be developed using Visual Studio 2017 from now to prevent project migration changes and issues.

 

If you are new to Asp.Net Core then I strongly recommend to read Beginning Asp.Net Core - Part 1 (Understanding the Basics) first. There are many changes between the traditional Asp.Net application and Asp.Net Core application. Reading that article will help you understand this article much better.

Visual Studio Community

There is a free community version of Visual Studio called Visual Studio Community for students, open-source and individual developers to start developing Asp.Net Core applications. I will use this community version in this article for demonstration. You can download and install it from here.

Let’s first create a new Asp.Net Core project and understand the project structure and the changes from Visual Studio 2015 project structure.

 

Getting Started with Asp.Net Core

Creating New Project

  1. Open Visual Studio 2017.

  2. Click File > New > Project.. This will bring “New Project” dialogue similar to below.

  1. Select .Net Core under Templates (left nav) and Asp.Net Core Web Application (.NET Core). Rename if required. Click OK.

  2. This will bring Asp.Net Core Templates like below, Choose Web Application and Click Change Authentication to “Individual User Accounts” to get a default AccountController implementation for us to start. Click OK.

This will create the new project and the solution explorer will look like this.

Note – If you did not see any file that is seen above then you might need to click “Show All files” icon on top nav bar of solution explorer (3rd icon from right).

If you have already seen an Asp.Net Core project template in Visual Studio 2015 then you can notice some differences here. Since 2017 format is the new default, we will not go deeper into the difference between 2015 and 2017 layout. Just for learning purpose, I will list some important changes (not all) that are made in the new layout for a better understanding.

Visual Studio 2017 New Project Layout

Looking at the solution explorer, the first thing we notice from previous versions of Asp.Net projects are, the Global.asax and Web.Config file are missing. This is because the fundamental requirement of Asp.Net Core is platform Independence or moving away from strong dependency on IIS webserver. Both are removed because of this and so, it is not available in the new structure. Though, Web.Config file was available in 2015 format, it was a very thin file to provide a default integration with IIS (not actually hosting). It contained a Asp.Net Core IIS module configuration to route the request to Kestrel web server in our Asp.Net Core application.

Note – Asp.Net Core application uses a new cross platform webserver called Kestrel. The IIS server will be just used as reverse proxy when deployed in Windows server and so the IISExpress in Visual Studio project template. We will see more about this in detail in an article for Asp.Net Core Hosting. For now, let’s understand Kestrel is a webserver for hosting Asp.Net Core application.

 Let’s understand the project layout and the default files (and structure) of Asp.Net Core solution.

  1. The new Program.cs file is the main entry point of our application. This is the file where the Kestrel webserver will be enabled and the application will be started to listening for incoming request.

public class Program

{

public static void Main(string[] args)

{

var host = new WebHostBuilder()

.UseKestrel()

.UseContentRoot(Directory.GetCurrentDirectory())

.UseIISIntegration()

.UseStartup<Startup>()

.UseApplicationInsights()

.Build();

 

host.Run();

}

}

This Main() method will setup the hosting environment with Kestrel and enables IIS integration. Meaning, Kestrel will get the requests from IIS webserver. The UseStartup<Startup>() method is called to setup the application pipeline configured in Startup class for the application level services. The UseApplicationInsights() configures application analytics to provide feedbacks on usages and performance monitors. Calling build will finally setup the host with all the configured items in the chain. This configuration follows chaining command pattern which allows us to call the different configuration methods precisely. The host.Run() will start the web application.

  1. The Startup class(Startup.cs) is where application pipeline is configured for request processing. This is similar to OWIN Startup class (Asp.Net Core Implementation of OWIN spec). Read this article here to know more about OWIN and Katana Implementation which helped (or evolved from) Asp.Net Core pipeline implementation.

This class primarily contain 2 methods,

  1. ConfigureServices()
  2. Configure()

 

Understanding Asp.Net Core Startup Class and Flow

ConfigureServices() Method

Default project template code,

 

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}


 

The ConfigureServices(IServiceCollection services) method will help the application to add the services required by the application into application IServiceCollection object. The services can be something that are specific to application or that are required to process the request. The ServiceCollection by default provide the Asp.Net core application with a dependency injection container implementation. The registered services will be automatically injected into the method parameters (or constructor parameters) when the runtime sees the service interface as a parameter. For example, the IEmailSender in AccountsController constructor will be injected with AuthMessageSender object during controller instantiation. Similarly, the ApplicationDbContext object is added to this collection to maintain one context object per request.

The call to services.AddMvc() adds many services that are required for MVC framework request handling. Similarly, you can add number of services you require in the application and access it across the application.

Configure() Method

The next method is the Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) method. Code below,

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseIdentity();

    // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}
 

This is where the application request pipeline is configured. The Asp.Net Core platform provides the implementation objects of these parameters when calling this method. The IApplicationBuilder and IHostingEnvironment object provide some of the essential functionalities to configure the application. The IApplicationBuilder object helps to add middleware components into the request pipeline.

These middlewares are similar to OWIN middleware (in Core application it is called Asp.Net core middlewares). Read this article to understand owin middleware here. The Asp.Net Core middlewares works similar to OWIN middleware and they are represented by RequestDelegate object. The middleware components provides various request filtering and request processing services by forming a pipeline in the order they are added here in this method. This pipeline formed here are responsible for processing the request and return the response back to the client.

The Use() method on IApplicationBuilder helps to add a middleware component on the request pipeline. Syntax below,

 

IApplicationBuilder.Use(Microsoft.AspNetCore.Http.RequestDelegate, Microsoft.AspNetCore.Http.RequestDelegate)

 

Note – All the middleware registration in the code above uses the registration extension method exposed by the middleware component instead of using the above syntax for code readability.

The IHostingEnvironment object gives details of executing environment whether it is dev, stage or prod. The IHostingEnvironment object provides some essential information about the hosting environment like EnvironmentName, ApplicationName, WebRootPath and ContentRootPath to configure the pipeline appropriately. For instance, the project template default code uses this object to add a detailed error page middleware component that gets added to the request pipeline only for dev environment.

ILoggerFactory provides functionality for application logging.

Startup Constructor

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

    if (env.IsDevelopment())
    {
        // For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
        builder.AddUserSecrets<Startup>();
    }
public IConfigurationRoot Configuration { get; }
    builder.AddEnvironmentVariables();
    Configuration = builder.Build();
}
 

We can add configuration values that are required by ConfigureService() and Configure() method using the Startup method constructor. By default, the configuration settings added in appsettings.json and appsettings.{env.EnvironmentName}.json file are read and made available to these methods using IConfigurationRoot object. The webserver environment dictionary will provide a value for the variable env.EnvironmentName mentioned above. The methods use Configuration public property of Startup method to read the populated configuration values. The IHostingEnvironment object is again provided by the runtime as constructor parameter to identify the environment details where the application is running.

To recap, the whole application Startup flow goes like this.

 

  1. All client side packages and images are now part of a folder called wwwroot. All Stylesheets, javascript files are by default copied here by the project template and package managers(Bower).

When publishing the project, the wwwroot folder will be web application’s root and all the application executable are kept outside this folder for security reasons. We will see more about this in an asp.net core hosting article.

  1. A new package manager called Bower for managing client side packages is part of Visual Studio now. This package manager is one of the widely used package manager in open source community for managing client side JavaScript and css packages.  This package manager uses the file bower.json similar to packages.config for Nuget packages.

bower.json

{

"name": "asp.net",

"private": true,

"dependencies": {

"bootstrap": "3.3.7",

"jquery": "2.2.0",

"jquery-validation": "1.14.0",

"jquery-validation-unobtrusive": "3.2.6"

}

}

The package manager will download all the package and put it under wwwroot folder. If you do not find these packages in the project, then right the file in solution explorer and click “Restore packages” to download the packages.

  1. bundleconfig.json file used to store the project bundling and minimification configuration for scripts and styles.

  2. The appsettings.json contains the appsettings that generally goes inside appSettings of Web.Config file.

  3. There is a special folder called Dependencies which does not present physically but categorically shows the project dependency packages added in the project.

  1. The project file in Visual Studio 2017 had major change when compared to Visual Studio 2015 and earlier version of project files. In 2015 project, the asp.net core project file is of extension .xproj. It additionally used a file called project.json to maintain the project dependencies and list the sdk and tooling dependencies of the project. These formats made the traditionally used MSBuild process not compatible with 2015 asp.net core projects. To address this issue and to migrate the existing larger projects the Visual Studio 2017 retired project.json, .xproj project file and an additional file called global.json and brought back the old project type file .csproj. Additionally, the visual studio 2017 project file is thin and simple when compared to older version project files. The package dependency are now part of project file. You can now edit and see the project file without having to unload the project( Project files can be edited only if the project is unloaded in older versions of IDE). The visual studio right click context menu now has edit option like below.

Almost, all configurations that are part of project.json are now part of this project file. In fact, it is a trimmed version where some of the sdk package reference that are explicitly part of project.json file are now considered implicitly included when sdk attribute is set on project node(bolded below).

The default project template file,

 

<Project Sdk="Microsoft.NET.Sdk.Web">

 

<PropertyGroup>

<TargetFramework>netcoreapp1.1</TargetFramework>

</PropertyGroup>

 

<PropertyGroup>

<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>

</PropertyGroup>

 

<PropertyGroup>

<UserSecretsId>aspnet-ASPNETCoreVS2017Demo-fda77520-71cd-4328-a391-c3548119f355</UserSecretsId>

</PropertyGroup>

<ItemGroup>

<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />

<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />

<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.1" />

<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.1" />

<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />

<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />

<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />

<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" PrivateAssets="All" />

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" PrivateAssets="All" />

<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" PrivateAssets="All" />

<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />

<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />

<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.0" PrivateAssets="All" />

<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />

</ItemGroup>

<ItemGroup>

<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />

<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0" />

<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />

</ItemGroup>

</Project>

 

 

Rest of the structure are same as old the project types and work similar to old project. Hope, reading this article gave a good kick-start to start developing Asp.Net Core projects.

Press F5 and see the default project in action!

In next part, let’s understand how to Asp.Net Core applications are hosted and using Kestrel webserver with IIS.



Feedback

Comments