.NET 6 New Features Using ASP.NET Core 6 and Visual Studio 2022
.NET 6 development is close to completion, and we will have a look at four new features which you can use in an ASP.NET Core 6 application.
It is due to be released in November 2021, alongside C# 10 and Visual Studio 2022.
As .NET 6 is still in preview, some features are still in development. However, the completed features can be used by downloading Visual Studio 2022 preview.
We will have a look at some of the completed features in this article.
Feature #1: Console template
The console template is one of the biggest changes in .NET 6.
Traditionally, a console application would have a namespace, class name and a Main
method. The Main
method would execute when the console application is started.
With an ASP.NET Core application, the Main
method is where web application instance would be built, configured and ran.
// Program.cs
using
Microsoft.AspNetCore.Hosting;
using
Microsoft.Extensions.Hosting;
namespace
RoundTheCode.DotNet6
{
public
class
Program
{
public
static
void
Main(
string
[] args)
{
CreateHostBuilder(args).Build().Run();
}
public
static
IHostBuilder CreateHostBuilder(
string
[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Main
method. As a result, the file just has the contents on the Main method included.// Program.cs
using
RoundTheCode.DotNet6;
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).Build().Run();
Feature #2: Merger of Program and the Startup class
In ASP.NET Core 5, there would be two classes to configure the application.
We looked at the Program
class above which builds and runs the application. In addition, there is a Startup
class, which is where we make our configuration changes, such as adding a database connection, or using dependency injection.
// Startup.cs
using
Microsoft.AspNetCore.Builder;
using
Microsoft.AspNetCore.Hosting;
using
Microsoft.Extensions.Configuration;
using
Microsoft.Extensions.DependencyInjection;
using
Microsoft.Extensions.Hosting;
namespace
RoundTheCode.DotNet6
{
public
class
Startup
{
public
Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public
IConfiguration Configuration {
get
; }
// This method gets called by the runtime. Use this method to add services to the container.
public
void
ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<ITeamService, TeamService>();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(
"v1"
,
new
() { Title =
"RoundTheCode.DotNet6"
, Version =
"v1"
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public
void
Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if
(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint(
"/swagger/v1/swagger.json"
,
"RoundTheCode.DotNet6 v1"
);
c.RoutePrefix =
string
.Empty;
});
}
else
{
app.UseExceptionHandler(
"/Home/Error"
);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name:
"default"
,
pattern:
"{controller=Home}/{action=Index}/{id?}"
);
});
}
}
}
However, with ASP.NET Core 6, these two classes have now been merged into the Program.cs
file.
And, with the elimination of namespace, class and Main method, it means we simply have one Program.cs file that creates the WebApplicationBuilder
instance, and configures the settings for the application.
// Program.cs
using
RoundTheCode.DotNet6;
var
builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<ITeamService, TeamService>();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc(
"v1"
,
new
() { Title =
"RoundTheCode.DotNet6"
, Version =
"v1"
});
});
var
app = builder.Build();
if
(app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint(
"/swagger/v1/swagger.json"
,
"RoundTheCode.DotNet6 v1"
);
c.RoutePrefix =
string
.Empty;
});
}
else
{
app.UseExceptionHandler(
"/Home/Error"
);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Feature #3: Minimal APIs
ASP.NET Core 6 introduces Minimal APIs.
With ASP.NET Core 5, it was possible to use the mapping methods inside the UseEndpoints
method. However, the only parameter that was able to be passed through that was the HttpContext
instance.
As a result, the MVC route would have been a better solution as dependencies can be passed in as parameters.
But with ASP.NET Core 6, there are new mapping methods as part of the WebApplication
instance. And, they allow you to pass in dependencies as methods.
For example, a WebApplication
instance is created when the application calls builder.Build()
.
var
app = builder.Build();
Within the WebApplication
instance, there are methods that represent each of the CRUD verbs.
MapGet
MapPost
MapPut
MapDelete
With that, we can go ahead and set up a new API endpoint by passing in the route, any parameters that we need, and then returning a response.
app.MapGet("team/{id}", (ITeamService teamService, int id) => teamService.Read(id));
If we were to run our API at this point, it would run fine. However, if we have Swagger configured for our Web API, it will not appear in the Swagger documentation.
We need to call the AddEndpointsApiExplorer
endpoint when building up our web application for it to appear in Swagger.
// Program.cs
var
builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
// Need to add this in for it to appear in Swagger.
builder.Services.AddSingleton<ITeamService, TeamService>();
...
var
app = builder.Build();
// Configure the HTTP request pipeline.
if
(builder.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint(
"/swagger/v1/swagger.json"
,
"RoundTheCode.DotNet6 v1"
));
}
app.MapGet(
"team/{id}"
, (ITeamService teamService,
int
id) => teamService.Read(id));
...
app.Run();
Feature #4: DateOnly and TimeOnly structs
A feature that is long overdue in .NET 6 is the ability to set an object that is just a date, or just a time.
Traditionally, the DateTime
struct would need to be used. That would mean that the time would exist, even if we were just using the date. And vice-versa with the time.
With the DateOnly
struct, we can add a new instance by specifying the year, month and day. And it has similar methods to the DateTime
struct.
Here is an example of how to add seven days to a DateOnly
struct.
var
dayNow =
new
DateOnly(2021, 08, 30);
var
dayWeekTime = dayNow.AddDays(7);
return
dayWeekTime.ToShortDateString();
It’s a similar story with TimeOnly
struct. Creating a new TimeOnly
struct allows for us to pass in parameters, such as hours, minutes and seconds.
From there, we can do things, like adding a certain number of hours. If the number of hours goes beyond 24, it will revert back to 0.
Here is an example of adding 8 hours to 18:00 in a TimeOnly
struct. The output would be 02:00.
var
timeNow =
new
TimeOnly(18, 0, 0);
var
timeNowEightHours = timeNow.AddHours(8);
return
timeNowEightHours.ToString();