Select Page

Implement Automapper in ASP.NET Core 3.1

In every application, there will be a need to map the object of one type to object of another type. Automapper is used to map these objects of dissimilar types without the need to write boring code to map each data member. Automapper in ASP.NET core comes handy when mapping objects between Model & DTO.

What is automapper & when to use it?

Automapper

Automapper is a popular small library for an object to object mapping used to map objects of different classes.

Let’s say you have two different classes EmployeeDTO & EmployeeModel with the same data members and you need to copy data from one object to another. Now you cannot set an instance of one object to another object as though data member match but objects are of different classes. You will have to use the traditional approach i.e. copy data from one object to another field by field.

To simply this job of mapping data from one object to objects Auto Mapper can be used. Auto Mapper is an open-source library and It maps the properties of two different objects.

Implement automapper in ASP.NET Core

Setup project for demonstration

Create new ASP.NET Core API Project

For implementation purpose, I have created a default ASP.NET Core 3.1 API Project using Visual Studio 2019

ASP.NET Core 3.1 API Project Creation

Install automapper NuGet package

Install package AutoMapper.Extensions.Microsft.DependencyInjection & this package depends on the AutoMapper package which will also get installed with this package.

NuGet Extension

Create sample EmployeeDTO & EmployeeModel classes

public class EmployeeDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public double Salary { get; set; }
}
public class EmployeeModel
{
    public long Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public double Salary { get; set; }
}

Configure automapper in startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
}
//Remaining code has been removed

Create an automapper mapping profile as EmployeeProfile.cs

Creating Mapping profiles is a better way to organize your object mappings. This profile tells automapper about which object links to which another object, which property of one object links to which property of other object and even let’s configure conditional mappings.

Create a mapping profile class that inherits from AutoMapper.Profile and add the configuration in the constructor of that mapping profile class

public class EmployeeProfile : Profile
{
    public EmployeeProfile()
    {
        CreateMap<EmployeeDTO, EmployeeModel>();
    }
}

Do note that with above CreateMap you can map from EmployeeDTO to EmployeeModel but reverse mapping will not be supported. Reverse Maps will have to be used for bidirectional mapping.

Using employee mapping in Employee controller

Create an Employee controller as shown below. Automapper service will have to be injected into controller using constructor dependency injection (Refer here for more details on Dependency Injection). Here Employee controller’s post method takes an object of type EmployeeDTO and using automapper maps it to EmployeeModel & return the same as JSON.

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    private readonly IMapper _mapper;

    public EmployeeController(IMapper mapper)
    {
        _mapper = mapper;
    }

    // POST api/<EmployeeController>
    [HttpPost]
    public IActionResult Post([FromBody] EmployeeDTO _employeeDTO)
    {
        var employeeModel = _mapper.Map<EmployeeModel>(_employeeDTO);
        return Ok(employeeModel);
    }
}

After executing the above project and calling Employee controller post-action using postman we got the following result

mapper in ASP.NET Core

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Mapping objects having different property names

To demonstrate how to map objects using automapper having different property names. We have modified class EmployeeModel property name for Name to FullName.

public class EmployeeDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public double Salary { get; set; }
}
public class EmployeeModel
{
    public long Id { get; set; }
    public string FullName { get; set; }
    public int Age { get; set; }
    public double Salary { get; set; }
}

Below is the code for Employee Mapping Profile class in which we have added mapping for properties having different name i..e. EmployeeDTO=>Name to EmployeeModel=>FullName. Here we have used method ForMember to specify the mapping

public class EmployeeProfile : Profile
{
    public EmployeeProfile()
    {
        CreateMap<EmployeeDTO, EmployeeModel>()
            .ForMember(empmodel => empmodel.FullName, empdto => empdto.MapFrom(empdto => empdto.Name));
    }
}

After executing the above project and calling Employee controller post-action using postman we got the following result.

automapper conditional mapping

Mapping objects with conditional mapping

Here is the how to implement conditional mapping for objects in Employee Mapping Profile class

public class EmployeeProfile : Profile
{
    public EmployeeProfile()
    {
        CreateMap<EmployeeDTO, EmployeeModel>()
            .ForMember(empmodel => empmodel.FullName, empdto => empdto.MapFrom(empdto => empdto.Name))
            .ForMember(empmodel => empmodel.Salary, empdto => empdto.MapFrom(empdto => empdto.Age > 55 ? empdto.Salary * 10 : empdto.Salary));
    }
}

Mapping objects with Null Subsitution

This allows you to set an default or alternate value for the destination data member in case the source data member is null

public EmployeeProfile()
{
    CreateMap<EmployeeDTO, EmployeeModel>()
        .ForMember(empmodel => empmodel.Telephone, empdto => empdto.NullSubstitute("Not Available"));
}

Mapping objects with Reverse Map

Do note that with CreateMap you can map from source (EmployeeDTO) to destination (EmployeeModel) but reverse mapping will not be supported. Reverse Maps will have to be used for bidirectional mapping.

public class EmployeeProfile : Profile
{
    public EmployeeProfile()
    {
        CreateMap<EmployeeDTO, EmployeeModel>()
            .ForMember(empmodel => empmodel.FullName, empdto => empdto.MapFrom(empdto => empdto.Name))
            .ForMember(empmodel => empmodel.Salary, empdto => empdto.MapFrom(empdto => empdto.Age > 55 ? empdto.Salary * 10 : empdto.Salary))
            .ReverseMap();
    }
}

Mapping Objects of Complex Types

EmployeeDTO & EmployeeModel classes have been modified to add a new property which is of a type of custom class as shown below

public class TelephoneNumberDTO
{
    //1 - Home, 2 - Office, 3 - Mobile
    public int PhoneType { get; set; }
    public string PhoneNumber { get; set; }
}
public class TelephoneNumberModel
{
    //1 - Home, 2 - Office, 3 - Mobile
    public int PhoneType { get; set; }
    public string PhoneNumber { get; set; }
}
public class EmployeeDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public double Salary { get; set; }
    public TelephoneNumberDTO Telephone { get; set; }
}
public class EmployeeModel
{
    public long Id { get; set; }
    public string FullName { get; set; }
    public int Age { get; set; }
    public double Salary { get; set; }
    public TelephoneNumberModel Telephone { get; set; }
}

Here is the how to map complex objects in Employee Mapping Profile class

public class EmployeeProfile : Profile
{
    public EmployeeProfile()
    {
        CreateMap<EmployeeDTO, EmployeeModel>()
            .ForMember(empmodel => empmodel.FullName, empdto => empdto.MapFrom(empdto => empdto.Name))
            .ForMember(empmodel => empmodel.Salary, empdto => empdto.MapFrom(empdto => empdto.Age > 55 ? empdto.Salary * 10 : empdto.Salary))
            .ReverseMap();
        CreateMap<TelephoneNumberDTO, TelephoneNumberModel>()
            .ReverseMap();
    }
}

Advantages of automapper

  • No need to write boring code so saves time
EmployeeDTO.Name = EmployeeModel.Name
EmployeeDTO.Id = EmployeeModel.Id
  • Handles conditional mapping
  • Handles complex objects

Summary

Automapper is used to map objects of dissimilar types without the need to write boring code to map each data member.

We used automapper in ASP.NET Core to map objects of dissimilar types. It was easy to map objects having the same property names with minimal coding. Though different property names required some mapping to map properties within classes.

Automapper on GitHub

You can also check my other article on Top 12 ASP.NET Core libraries for developers – https://procodeguide.com/programming/top-12-essential-asp-net-core-libraries

Complete source code for Automapper in ASP.NET Core is available on my GitHub for download

Automapper.Sample
https://github.com/procodeguide/Automapper.Sample
0 forks.
0 stars.
0 open issues.
Recent commits:

  1. What is Automapper?

    Automapper is a popular small library for an object to object mapping used to map objects of different classes.

  2. When to use Automapper?

    Use it to simply job like you have two different classes EmployeeDTO & EmployeeModel with the same data members and you need to copy data from one object to another

  3. What are advantages of Automapper?

    No need to write boring code so saves time, handles complex objects, conditional mapping, etc.

Web API Versioning in ASP.NET Core 3.1

Web API versioning though being one of the important features mostly goes ignored when building a Web API application. Web API versioning in ASP.NET Core is simple and easy to implement. In this article, we will look at what is Web API versioning along with why it is needed and how to achieve Web API versioning in ASP.NET Core.

Web API Versioning – What & Why?

Web API Versioning in ASP.NET Core

When we start a new Web API project at that time it is all fine as we develop an initial version get it tested with all the clients and push it to production. Now after going to production there are some changes in existing Web API which is a mandatory change for one of the client but it will not be possible for other clients to go live with new changes at the same time. Also, it is not recommended to change existing working clients by implementing new Web API features which they don’t need. So now if you change the existing Web API on production with breaking changes and if any of the clients don’t implement new changes it will start failing. So now there is a need to support the old version as well as the newer version of Web API so that all existing clients can function without failing.

Web API versioning is a feature using which we can implement multiple versions of the same API so that different clients can work with the required version of API.

Implement Web API versioning in ASP.NET Core

There are many ways to implement versioning in Web API. Here I will be covering methods to version Web API using Nuget Package Microsoft.AspNetCore.Mvc.Versioning

We can achieve Web API versioning using following approaches

  1. Query String
  2. HTTP Header
  3. URL

Here for demonstration purposes, I have created an ASP.NET Core 3.1 Web API project. I will be using the default generated WeatherForecast Controller for configuring Web API Versioning. By default, there is no versioning support added as shown below.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    public string Get()
    {
        return "This is from WeatherForecast - Sunny Day";
    }
}

As shown below Install the Nuget package and enable the support for Web API versioning in ConfigureServices method in the startup.cs file.

Web API Versioning in ASP.NET Core 3
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddApiVersioning(apiVerConfig =>
        {
            apiVerConfig.AssumeDefaultVersionWhenUnspecified = true;
            apiVerConfig.DefaultApiVersion = new ApiVersion(new DateTime(2020, 6, 6));
        });

        services.AddControllers();
    }
    //Remaining code has been removed
}

Web API Versioning using Querystring Parameter

Here you need to define the same controller twice with different values for ApiVersion attribute in different namespaces as shown below.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}

namespace WebAPIVersioning.Controllers.V2
{
    [ApiVersion("2.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V2 - Rainy Day";
        }
    }
}

After defining multiple versions of the same controller as shown above you can invoke these using version number in querystring i.e. http://server:port/api/weatherforecast?api-version=1.0 or 2.0.

Below is the output screen from both version of API

Versioning Using Querystring

Web API Versioning using HTTP Header

For version to be part of HTTP header and enable controllers to read version number form header you need to first configure this in ConfigureServices method in startup.cs file as hows below

public void ConfigureServices(IServiceCollection services)
{
    services.AddApiVersioning(apiVerConfig =>
    {
        apiVerConfig.AssumeDefaultVersionWhenUnspecified = true;
        apiVerConfig.DefaultApiVersion = new ApiVersion(1,0);
        apiVerConfig.ReportApiVersions = true;
        apiVerConfig.ApiVersionReader = new HeaderApiVersionReader("api-version");
    });

    services.AddControllers();
}

Then you need to define the same controller twice with different values for ApiVersion attribute in different namespaces as shown below.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}

namespace WebAPIVersioning.Controllers.V2
{
    [ApiVersion("2.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V2 - Rainy Day";
        }
    }
}

After defining multiple versions of the same controller as shown above you can invoke these using version number in HTTP header as “api-version: 1.0” or “api-version: 2.0”

Below is the output screen from both version of API

Versioning Using HTTP Header

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Web API Versioning using URL

Here you need to define the same controller twice with different values for ApiVersion attribute in different namespaces as shown below. Also, you need to add parameter v{version:apiVersion} in route attribute like Route(“api/v{version:apiVersion}/[controller]”) so that API version becomes part of URL.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}

namespace WebAPIVersioning.Controllers.V2
{
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V2 - Rainy Day";
        }
    }
}

After defining multiple versions of the same controller as shown above you can invoke these using version number in HTTP URL i.e. http://server:port/api/v1/weatherforecast or http://server:port/api/v2/weatherforecast

Below is the output screen from both version of API

Versioning in ASP.NET Core using HTTP URL

Supported & Deprecated API Version

In startup.cs file while configuring API version since we have enabled parameter ReportApiVersions so any API call will return supported versions for that API in response header as shown below.

Supported API Versions

Now if you want to pass on the information that older version of API will be removed then you can add deprecate flag to the version of API which will be not supported in future.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0",Deprecated = true)]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}
deprecated API versions

Summary

API versioning is useful when you need to enhance an existing API that is being consumed by multiple clients. Versioning in ASP.NET Core Web API is easy and simple to implement. We can make use of the NuGet package Microsoft.AspNetCore.Mvc.Versioning to achieve the same.

Though there are a number of ways to achieve API versioning I personally prefer URL based versioning as that avoids extra api-version parameter in querystring and HTTP header both.

You can also check my other article on Top 12 ASP.NET Core libraries for developers: https://procodeguide.com/programming/top-12-essential-asp-net-core-libraries/

Dependency Injection in ASP.NET Core 3.1

ASP.NET Core is designed from the ground up to support the dependency injection design pattern. Dependency injection in ASP.NET Core provides a technique to create applications that are loosely coupled. ASP.NET Core allows us to injects dependent objects either through constructor or methods. Based on the environment in which application is running different objects can be injected.

What is Dependency Injection?

Dependency Injection in ASP.NET Core

Dependency injection is used to achieve loose coupling in application between client objects and their dependencies. Instead of directly instantiating objects of classes and using them rather Interfaces are used and objects are provided to class in some way either through constructor or through some method.

Class will depend on an interface rather than concrete implementation of dependencies. This makes our code loosely coupled as we can inject any concrete object as far as it implements the required interface.

ASP.NET Core comes with a built-in container (IServiceProvider) that supports constructor injection by default and the framework itself provides some services through dependency injection.

Here is a great article by Martin Fowler on Dependency Injection

Why Dependency Injection

  • Improves code readability by keeping it clean so easier to maintain it.
  • Application is loosely coupled which improves flexibility
  • Increases code testing ability with different mock implementations of services.
  • Supports extendable class structure

Implementation of Dependency Injection in ASP.NET Core

There are two types of services in ASP.NET Core

  1. Framework provided services – These services are part of ASP.NET Core framework
  2. Custom Services – These are add-on services created by developers

Registering framework provided services

Below code shows how to register a framework defined service (Memory Caching Service) in ConfigureServices() method of startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddMemoryCache();
    }
    //Remaining code was removed
}

Registering your own services

You can even register your own service and make it available for dependency injection into controllers, middlewares & views

First you need to define the service using an interface as shown below

public interface ILogger
{
    void Log(string message);
    void Log(string message, Exception ex);
}

public class LogToFile : ILogger
{
    public void Log(string message)
    {
        //Log Message to File
    }

    public void Log(string message, Exception ex)
    {
        //Log Message & Exception to File
    }
}

public class LogToDB : ILogger
{
    public void Log(string message)
    {
        //Log Message to Database
    }

    public void Log(string message, Exception ex)
    {
        //Log Message & Exception to Database
    }
}

Service Lifetime

Before we talk about how to register your service in the startup.cs file we need to understand the significance of Service Lifetime while registering a service. Lifetime decides whether the instance of a service being injected is a new instance to that component or it same signal instance which is being used everywhere in the application. While registering service you need to choose the lifetime for each service.

Transient – Each time a new object is created for dependency injection i.e. each component will have its unique instance. This is the option used for lightweight & stateless services.

services.AddTransient<ILogger, LogToFile>();

Scoped – This means that it is created once for each request i.e. a new object will be created for each new request.

services.AddScoped<ILogger, LogToFile>();

Singleton – One single component will be created for the lifetime of the application. This single instance is shared across all components and all requests.

services.AddSingleton<ILogger, LogToFile>();

Registering Services in startup.cs

You need to register required services in ConfigureServices() methif of startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddMemoryCache();

        services.AddTransient<ILogger, LogToDB>();
    }
     //Remaining code has been removed
}

Once service is registered it can be injected into other components

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Injecting services (framework/custom) into Middleware

Below is the code sample for the injection of service into middleware. Either it can be injected through the constructor and or through the invoke method.

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    private readonly IMemoryCache _cache;

    public ExceptionMiddleware(RequestDelegate next, ILogger logger, IMemoryCache cache)
    {
        _next = next;
        _logger = logger;
        _cache = cache;
    }

    public Task Invoke(HttpContext httpContext, ILogger logger, IMemoryCache cache)
    {
        return _next(httpContext);
    }
}

Injecting services (framework/custom) into Controller

Below is the code sample for the injection of service into the controller. Either it can be injected through the constructor and or through the action method.

public class HomeController : Controller
{
    private readonly ILogger _logger;
    private readonly IMemoryCache _cache;

    public HomeController(ILogger logger, IMemoryCache cache)
    {
        _logger = logger;
        _cache = cache;
    }

    public IActionResult Index([FromServices] ILogger logger, [FromServices] IMemoryCache cache)
    {
        return View();
    }
}

Request Services Manually

Instead of using dependency injection to inject services, components can even manually request services. Below code shows how to manually request a service using HttpContext object.

var services = this.HttpContext.RequestServices;
var log = (ILogger)services.GetService(typeof(ILogger));

Summary

Dependency injection allows us to develop loosely coupled applications. Dependency injection in ASP.NET Core is supported and also it is simple and easy to implement. Framework provided services or your own custom services can be used for dependency injection.

You can inject components using dependency injection into middlewares, controllers, view, etc. Service lifetime can be used to control the scope of the service.

References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1

You can also check my another Article on Microsoft Azure Storage – https://procodeguide.com/programming/azure-storage/

ASP.NET Core Hosting on IIS on Windows

This article will provide details about how to deploy ASP.NET Core web application or Web API on IIS. .NET Core 2.2 onwards there were significant changes in the hosting model to IIS. Prior to .NET Core 2.2 only option available was to use IIS as a reverse proxy to ASP.NET Core Kestrel web server.

With .NET Core 2.2 option was added for direct in-process hosting to IIS which can improve the performance significantly. The old approach with IIS as a reverse proxy is also available as an out-of-process hosting model. ASP.NET Core hosting on IIS supports two models for hosting on the IIS i.e. In-process hosting model & out-of-process hosting model.

ASP.NET Core Hosting on IIS is different as compared to ASP.NET Webforms or ASP.NET MVC.

Supported operating systems

  • Windows 7 or later
  • Windows server 2012 R2 or later

Apps published for 32 bit (x86) and 64 bit (x64) are supported. Application pools under should be configured accordingly for 32-bit & 64-bit apps.

Type of Hosting Models

ASP.NET Core Hosting on IIS
In-process hosting model

As the name suggests ASP.NET Core application runs in the same process as IIS (w3wp.exe) and it does not use the Kestrel web server. This hosting model uses IISHttpServer that is hosted directly inside the application pool. This hosting model provides a significant performance improvement over the other out-of-process model as it does not have to forward requests to Kestrel for processing. IIS handles process management in the traditional way with the help of windows process activation service (WAS).

Request flow details:

  • Request arrives from web at HTTP.sys
  • HTTP.sys routes the request to IIS
  • ASP.NET Core module receives the request and passes it to IIS HTTP Server.
  • IISHTTPServer passes the request to ASP.NET Core middleware pipeline.
  • ASP.NET Core application handles the request and returns response
Out-of-process hosting model

Again as the name suggests here ASP.NET Core application runs in a separate process from the IIS worker process. This is similar to what was available in ASP.NET Core versions prior to 2.2. ASP.NET Core module handles process management, this module is responsible for starting the ASP.NET Core App when the first request arrives & also restarts if it shuts down or crashes.

Here IIS acts as a reverse proxy to Kestrel web server where ASP.NET Core application is running. For application to be configured as an out-of-process model it needs to specify UseIISIntegration as part of WebHostBuilder code in the program.cs file. ASP.NET Core module doesn’t support HTTPS forwarding.

Steps for ASP.NET Core hosting on IIS

  • Publish your application
  • Install/Enable IIS Server on windows
  • Install the .NET Core Hosting Bundle
  • Create a new application pool & website for your application
Publish your Application

Right click on project in Solution Explorer and select Publish option. This should being up the screen to publish your code. Select Folder option to publish all the files to a folder. This folder will be configured in IIS as website folder.

IIS Configuration

Use the Add Roles & Features wizard to Enable IIS on Windows server operating system.

IIS Installation

Select following services under web server on Select role services screen

IIS Installation Select Services

For windows desktop operating system select Control Panel ==> Program ==> Program & Features ==> Turn windows features on or off and select following features for installations

IIS Installation on Windows Desktop

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Install .NET Hosting Bundle

You can download the required version of the hosting bundle from here. The hosting bundle installs the .NET Core Runtime, .NET Core Library, and the ASP.NET Core Module.

If the Hosting Bundle is installed before IIS, the bundle installation must be repaired. Run the Hosting Bundle installer again after installing IIS.

Create IIS Site

Open IIS Manager & expand server node in connections panel on left hand side. Right click site folder and select “Add Website…” from the context menu. Provide Site name, Physical path (where code was published), binding configuration details & click on ok.

IIS Create Website

Finally browse and check your website.

Summary

ASP.NET Core hosting on IIS supports two models for hosting on the IIS i.e. In-process hosting model & out-of-process hosting model. .NET Core 2.2 onward there were significant changes in the hosting model to IIS.

In-process hosting model provides a significant performance improvement over the other out-of-process model as it does not have to forward requests to Kestrel for processing

References: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/?view=aspnetcore-3.1

You can also check my other Article on Configuring multiple ASP.NET Core hosting environment: https://procodeguide.com/programming/asp-net-core-hosting-environment/

Configuring multiple ASP.NET Core hosting environment

The hosting environment is used to indicate at runtime on which environment an ASP.NET Core application is running. There can be multiple values for ASP.NET Core Hosting Environment and it can be set any single value. Framework provided environments are Development, Staging & Production but you can specify your own values as well i.e. Testing, QA, PrePROD, etc.

Environment variables allow the application to be configured as per the environment on which the application is running.

Why hosting environments?

As part of the software development life cycle, an application has to go through various phases like Development, Testing & Production. We as application developers need to configurable different parameters for different environments like each environment will have its own database, different versions (URL) of third party services, different application features like logging, bundling, minification, exception handling, etc based on the environment on which application is running.

Multiple environments let us control these parameters at runtime i.e database connections, third party service URLs, application behavior like logging, exception, bundling, etc can be configured environment wise.

How to set ASP.NET Core hosting environment

There are a number of ways to set the hosting environment variable. This environment variable needs to be set on each machine on which the application is required to run.

using windows command line

To set the environment variable from command prompt following command can be used. This is applicable for the current session and app should be started using dotnet run command.

set ASPNETCORE_ENVIRONMENT=Staging
dotnet run --no-launch-profile
using PowerShell
$Env:ASPNETCORE_ENVIRONMENT = "Staging"
dotnet run --no-launch-profile

Both command line & PowerShell sets environment variables only for the processes launched from that command window.

using windows environment variables

To set ASP.NET Core hosting environment variable globally in windows so that each you don’t need to set it before running the application use windows environment variables

To set environment variable globally open Control Panel ==> System ==> Advanced System Settings ==> Environment Variables ==> System Variable New/Edit & set values in ‘name’ and ‘value’ and click ok

ASP.NET Core Hosting Environment
using web.config

Environment variable can be set in web.config as well. Environment variable can be specified for the process in the processPath attribute in web.config. Environment variables set in this section takes precedence over system environment variables.

<aspNetCore processPath="dotnet"
      arguments=".\MyApp.dll"
      stdoutLogEnabled="false"
      stdoutLogFile=".\logs\stdout"
      hostingModel="inprocess">
  <environmentVariables>
    <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
  </environmentVariables>
</aspNetCore>

Developers can use this to temporarily set the environment to development for debugging purpose.

using Per IIS Application Pool

Environment variable can also be set at IIS pool level for an application running in an isolated application pool. When ASPNETCORE_ENVIRONMENT is set for an app pool, its value overrides a setting at the system level.

<applicationPools>
   <add name="CorePool" managedRuntimeVersion="v4.0" managedPipelineMode="Classic">
      <environmentVariables>
         <add name="ASPNETCORE_ENVIRONMENT" value="Staging" />
      </environmentVariables>
   </add>
</applicationPools>

AppCmd.exe can be used to add environment variables to the IIS pool level.

appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='CorePool'].environmentVariables.[name='ASPNETCORE_ENVIRONMENT',value='Staging']" /commit:apphost

Restart IIS or Server after setting the environment variable in IIS for application to have new values available after changes.

How to use ASP.NET Core hosting environment in your application

Environment based startup class

Below is the example of environment based startup class in which IWebHostingEnvironment has been injected to identify the environment and add code behavior accordingly.

public class Startup
{
    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        Configuration = configuration;

        var builder = new ConfigurationBuilder()
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    }

    //Remaining code was removed

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
        app.UseStaticFiles();

        app.UseRouting();

        //Remaining code was removed
    }
}

In the above code in startup constructor environment variable name has been used to load appsettings.json file based on the environment configured for application i.e. appsettings.development.json or appsettings.production.json file will be used based on the environment set at runtime.

Also in the configure method different application behavior has been set for exception handling based on the environment set at runtime.

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

How to add custom ASP.NET Core hosting environment

Development, Staging & Production environments are provided by default by the framework. But ASPNETCORE_ENVIRONMENT can be set to any string value. To configure your own environments following extension class can be added.

public static class HostingEnvironmentExtensions
{
    public const string QAEnvironment = "QA";
    public const string UATEnvironment = "UAT";
    public const string TestEnvironment = "Test";

    public static bool IsQA(this IWebHostEnvironment hostingEnvironment)
    {
        return hostingEnvironment.IsEnvironment(QAEnvironment);
    }

    public static bool IsUAT(this IWebHostEnvironment hostingEnvironment)
    {
        return hostingEnvironment.IsEnvironment(UATEnvironment);
    }

    public static bool IsTest(this IWebHostEnvironment hostingEnvironment)
    {
        return hostingEnvironment.IsEnvironment(TestEnvironment);
    }
}

Once configured it will be available under environment variable as shown below

ASP.NET Core Custom Environments

Summary

ASP.NET Core hosting environment allows developers to control how their application will behave in different environments. There are multiple ways available to set an environment variable for the application.

ASP.NET Core hosting has provided 3 environments i.e. development, staging & production by default but custom environments can be added and practically any string can be used for environment name.

References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-3.1

You can also check my other Article on ASP.NET Core bundling & minification: https://procodeguide.com/programming/asp-net-core-bundling-minification/

ASP.NET Core Bundling & Minification

Bundling & Minification are two distinct techniques that can be used to improve page load performance for web applications. In ASP.NET Core bundling & minification is not available by default and needs to be implemented for a web application.

In ASP.NET Core web apps client-side resources (.js & .css files) can be bundled and minified to improve performance. Bundling & minification can be done at design time as well as at runtime. Third-party tools like Gulp & Grunt can be used for design-time bundling & minification. In design-time, minified files are created before deployment which can add complexity to the build process but reduces server load.

Bundling & minification techniques were introduced to improve page load time by not only reducing the number of requests to the server but also reducing the size of data transfer from the server to the browser.

asp.net core bundling and minification

Bundling is a technique that combines multiple files into a single file to reduce the number of requests made to the server to download files. Bundles mainly for CSS & JavaScript can be created. Fewer .css & .js files means fewer HTTP requests from browser for these files. Any number of individual bundles can be created but again these bundles also should be small in number.

Minification removes unnecessary characters, comments, spaces & new lines from code without altering core functionality. This reduces the size of mainly CSS & js files significantly. Reduced size means less amount of data to be downloaded over HTTP.

ASP.NET Core bundling & minification should be generally enabled only in the production environment and for the non-production environments used original files which makes it easier to debug.

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

ASP.NET Core bundling and minification implementation

Add Bundler & Minifier extension to the visual studio. This extension will add the feature to the visual studio using which you can right-click any js or CSS file in solution explorer and create a minified version of that file by selecting the option “Bundler & Minifier => Minify File”

Bundler And Minifier
Minify File

As shown above perform this minify file to explicitly minify CSS & JS files If you don’t want to bundle and just directly use the minified version of static files.

ASP.NET Core bundling & minification support bundleconfig.json file. Shown below is the bundleconfig.json this file contains the information about what files to bundle and minify.

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/site.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

The bundleconfig.json file defines the options for each bundle

  • outputFileName : The name of the bundle file to output.
  • inputFiles : An array of files to bundle together. It can contain multiple files to form one bundle.
  • minify : The minification options for the output type

We can add our custom application-specific CSS & js files to form one minified bundle. To create outputFileName on the build you need to right-click on bundleconfig.json file in solution explorer and select “Bundler & Minifier => enable bundle on build..”.

Enable bundle on build

ASP.NET Core bundling & minification files specified in outputFileName are recommended to be used in the production environment only. You can specify which files to include in your page.

<environment include="Development">
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</environment>

<environment exclude="Development">
    <link rel="stylesheet" href="~/css/site.min.css" />
</environment>

Summary

ASP.NET Core bundling & minification is not enabled by default we need to configure this for bundling & minification of client-side resources.

Bundling and minification improves the page request load time. Bundling allows to reduce number requests per page by combining multiple client-side resource files into single file. Minification reduces size of these client-side resources to reduce number of bytes downloaded per request.

References: https://docs.microsoft.com/en-us/aspnet/core/client-side/bundling-and-minification?view=aspnetcore-3.1

You can also check my other Article on ASP.NET Core Caching: https://procodeguide.com/programming/aspnet-core-caching/