Welcome to today’s blog.
In today’s blog I will be explaining the structure of a typical .NET Core Web API application. Moreover, I will explain (with examples) the two different types of .NET Core application:
- A generically hosted .NET Core application
- A web hosted .NET Core application
To create the above, the simplest way is to use the Visual Studio templates. If you are using Visual Studio 2017 or 2019, or as recent as Visual Studio 2022, the templates are available when you select the New Project … option in the IDE menu.
Using the Visual Studio Project Templates
When applications are created within Visual Studio there are a few ways we can use to create a skeleton .NET Core application. The quickest way is to use one of the many application templates that are available. With .NET Core, there are many different types of .NET Core applications that we can create, including Console, ASP.NET Web API, ASP.NET MVC, Hosted Worker Processes, ASP.NET Identity applications and so on.
Below is an example of a template for a console application.
Installing NuGet Packages in a .NET Core Web API Application
With a basic template application, you have the starting point for implementing a useful application, however, to build more sophisticated applications, it helps to make use of the variety of NuGet packages that are available that gives you as a developer the power to utilise many different services and tools that are available at your disposal. These include Entity Framework Core, Relational Database Providers, Identity Providers, Azure Services and so on.
Installing a NuGet package requires accessing the Package Installer within Visual Studio.
Then search for the package and install it:
Once installed, the projects CSPROJ file is updated with the package name and version. Any packages are then included within application builds.
Understanding .NET Core Application Pipelines
An ASP.NET Core application and a .NET Core Console applications have some differences that allow either a Web application to be running or a Console application to be running.
In the example below within the Program.cs, we have the application start up for a web application that uses the wen host builder pipeline IWebHostBuilder as shown:
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
namespace BookLoanScheduledHostedServiceApp
{
static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
The web host builder allows us to use the Startup class to bootstrap our web application.
Below is a typical structure that is used for the Startup class with Configure() and ConfigureServices() methods used to configure application settings, dependency injection, and initializing middleware with extension methods:
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using BackgroundTasksSample.Services;
namespace BookLoanScheduledHostedServiceApp
{
public class Startup
{
private IConfiguration _configuration;
private readonly IServiceProvider _provider;
public IServiceProvider serviceProvider
{
get { return this._provider; }
}
public IConfiguration configuration
{
get { return this._configuration; }
}
public Startup()
{
var services = new ServiceCollection();
this.ConfigureServices(services);
services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
public void ConfigureServices(IServiceCollection services)
{
}
}
}
The types of middleware libraries we can initialize here include Entity Framework Core, Identity Providers, and Database Providers. In ConfigureServices() we can configure dependency injection IOC containers with interfaces and classes within the Service Collection of the IServiceProvider.
When our application is not a web application or web API application, such as a console application, since .NET Core 3.0, we can use the Generic Host Service, IHostBuilder to build the bootstrap start up for the application. Below is the code used to chain the ConfigureServices() method of the application pipeline to allow the application to be configured as we did for a web application:
using System;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
namespace BookLoanEventHubSenderConsoleApp
{
class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json",
optional: true, reloadOnChange: false);
IConfiguration config = configBuilder.Build();
// configure IOC containers here..
…
});
}
}
In the following sections we will using the start up for a web application for our Web API service applications.
Application Configuration Settings
Using appSettings.json for Application Settings
The application settings for a .NET Core application contains specific keys and values that are used to configure services and providers that are used within the application. These can include the following:
- Database providers such as database connectivity to SQL Server databases,
- Configurations for application users and roles,
- Numeric thresholds including defaults for parameters,
- Security providers such as Identity Server,
- URIs for Web API services that are used within the API,
- Azure cloud service PaaS connectivity.
Below is an example of the content of a typical appsettings.json file that contains two JSON keys and corresponding values:
{
"ScheduleSettings": {
"pollingInterval": 40
},
"ConnectionStrings": {
"AppDbContext": "Server=localhost;Database=aspnet-BookCatalog;User Id=????; Password=????;"
}
}
In a console or service application we can read the above configuration value for the key pollingInterval using the ConfigurationBuilder class:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
…
public Startup()
{
var services = new ServiceCollection();
this.ConfigureServices(services);
services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
public void ConfigureServices(IServiceCollection services)
{
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
IConfiguration config = configBuilder.Build();
this._configuration = config;
var pollingInterval = 0;
try
{
pollingInterval = this._configuration
.GetSection("ScheduleSettings")
.GetValue<int>("pollingInterval");
}
catch (Exception ex)
{
pollingInterval = 30;
}
..
We have been able to read our appsettings.json configuration from the current directory with the configuration builder by using the AddJsonFile() extension method, which is available from the NuGet package Microsoft.Extensions.Configuration.Json.
In an ASP.NET Core Web API application, the retrieval of configuration settings is even simpler and transparent with dependency injection and the IConfiguration service providing configuration settings directly after injection from the Startup constructor. There is no need to include the JSON configuration package as this is provided within the Microsoft.AspNetCore.Builder package that comes with the ASP.NET Core Web API template. The same configuration retrieval is shown in the sample code below:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Http;
…
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var pollingInterval = 0;
try
{
pollingInterval = Configuration
.GetSection("ScheduleSettings")
.GetValue<int>("pollingInterval");
}
catch (Exception ex)
{
pollingInterval = 30;
}
…
Dependency Injection and Service Collections
Explaining dependency injection and service collections
In our .NET Core Web API application, apart from a Startup class and an API controller class, we may also be using custom classes that are used to provide custom responses to API controller methods.
To be able to use these classes within .NET Core Web API requests, we will need to inject instances of these classes within our controller and possibly within other custom classes. To be able to inject these instances requires the use of the built-in .NET Core Dependency Injection IOC library:
Microsoft.Extensions.DependencyInjection;
Without being able to inject class instances, we would have problems keeping track and of disposing and creating these class instances throughout the lifetime of a HTTP request or a worker thread. In addition, we have one of the SOLID principles catered for within our application, and support for interface segregation.
That brings me to explain how we can decouple concrete custom service classes from the application instance.
Using Dependency Injection to Decouple Web API Application Dependencies
Suppose we have a custom class BookService, which is extended by an Interface IBookService:
public interface IBookService
{
Task<List<BookViewModel>> GetBooks();
}
public class BookService: IBookService
{
readonly ApplicationDbContext _db;
public BookService(ApplicationDbContext db)
{
_db = db;
}
public async Task<List<BookViewModel>> GetBooks()
{
List<BookViewModel> books = await _db.Books.ToListAsync();
return books;
}
…
}
And a basic application configuration class AppConfiguration, which is extended by an interface IAppConfiguration:
public interface IAppConfiguration
{
string AppName { get; set; }
string AppVersion { get; set; }
}
public sealed class AppConfiguration : IAppConfiguration
{
public AppConfiguration() { }
public string AppName { get; set; }
public string AppVersion { get; set; }
}
To add the above custom classes to the IOC container collection, ServiceCollection, we use some useful extension methods. Below is code that shows how this is done:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IBookService, BookService>();
services.Configure<AppConfiguration>
(Configuration.GetSection("AppSettings"));
}
}
I have not yet explained what the extension method AddTransient() does. It is an application lifetime service, which controls how long the instanced custom class will be in scope until it is disposed. I explained the different types of lifetime scopes of classes and dependency injection in an earlier post.
In earlier posts, I explained how to use application settings in more detail.
The above was an introduction into what the basic constituents of a .NET Core Web API application are.
That is all for today’s post.
I hope you have this post useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.