Today we’re very happy to announce that the second preview of the next minor release of ASP.NET Core and .NET Core is now available for you to try out. This second preview includes many refinements based on feedback we received from the first preview we released back in February.
You can read about .NET Core 2.1.0-preview2 over on their blog.
You can also read about Entity Framework Core 2.1.0-preview2 on their blog.
How do I get it?
You can download the new .NET Core SDK for 2.1.0-preview2 (which includes ASP.NET Core 2.1.0-preview2) from https://www.microsoft.com/net/download/dotnet-core/sdk-2.1.300-preview2
Visual Studio 2017 version requirements
Customers using Visual Studio 2017 should also install (in addition to the SDK above) and use the Preview channel (15.7 Preview 3 at the time of writing) when working with .NET Core and ASP.NET Core 2.1 projects. .NET Core 2.1 projects require Visual Studio 2017 15.7 or greater.
Impact to machines
Please note that given this is a preview release there are likely to be known issues and as-yet-to-be-discovered bugs. While .NET Core SDK and runtime installs are side-by-side on your machine, your default SDK will become the latest version, which in this case will be the preview. If you run into issues working on existing projects using earlier versions of .NET Core after installing the preview SDK, you can force specific projects to use an earlier installed version of the SDK using a global.json file as documented here. Please log an issue if you run into such cases as SDK releases are intended to be backwards compatible.
Already published applications running on earlier versions of .NET Core and ASP.NET Core shouldn’t be impacted by installing the preview. That said, we don’t recommend installing previews on machines running critical workloads.
Announcements and release notes
You can see all the announcements published pertaining to this release at https://github.com/aspnet/Announcements/issues?q=is%3Aopen+is%3Aissue+milestone%3A2.1.0-preview2
Release notes, including known issues, are available at https://github.com/aspnet/Home/releases/tag/2.1.0-preview2
Giving feedback
The main purpose of providing previews like this is to solicit feedback from customers such that we can refine and improve the changes in time for the final release. We intend to ship a release candidate in about a month (with “go-live” license and support) before the final RTW release.
Please provide feedback by logging issues in the appropriate repository at https://github.com/aspnet or https://github.com/dotnet. The posts on specific topics above will provide direct links to the most appropriate place to log issues for the features detailed.
New features
You can see a summary of the new features planned in 2.1 in the roadmap post we published previously.
Following are details of additions and changes in preview2 itself.
Improvements to Razor UI libraries
New in ASP.NET Core 2.1 is support for building Razor UI in class libraries. In Preview 2 we’ve made various improvements to simplify authoring Razor UI in class libraries through the introduction of the new Razor SDK.
To create a Razor UI class library, start with a .NET Standard class library and then update the SDK in the .csproj file to be Microsoft.NET.SDK.Razor
. The Razor SDK adds the necessary build targets and properties so that Razor files can be included in the build.
To create your own Razor UI class library:
- Create a .NET Standard class library
dotnet new classlib -o ClassLibrary1
- Add a reference from the class library to
Microsoft.AspNetCore.Mvc
dotnet add ClassLibrary1 package Microsoft.AspNetCore.Mvc -v 2.1.0-preview2-final
- Open
ClassLibrary1.csproj
and change the SDK to beMicrosoft.NET.SDK.Razor
<Project Sdk="Microsoft.NET.Sdk.Razor"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.0-preview2-final" /> </ItemGroup> </Project>
- Add a Razor page and a view imports file to the class library
dotnet new page -n Test -na ClassLibrary1.Pages -o ClassLibrary1/Pages dotnet new viewimports -na ClassLibrary1.Pages -o ClassLibrary1/Pages
- Update the Razor page to add some markup
@page <h1>Hello from a Razor UI class library!</h1>
- Build the class library to ensure there are no build errors
dotnet build ClassLibrary1
In the build output you should see both
ClassLibrary1.dll
andClassLibrary1.Views.dll
, where the latter contains the compiled Razor content.
Now let’s use our Razor UI library from an ASP.NET Core web app.
- Create a ASP.NET Core Web Application
dotnet new razor -o WebApplication1
- Create a solution file and add both projects to the solution
dotnet new sln dotnet sln add WebApplication1 dotnet sln add ClassLibrary1
- Add a reference from the web application to the class library
dotnet add WebApplication1 reference ClassLibrary1
- Build and run the web app
cd WebApplication1 dotnet run
- Browse to
/test
to see your page from your Razor UI class library
Looks great! Now we can package up our Razor UI class library and share it with others.
- Create a package for the Razor UI class library
cd .. dotnet pack ClassLibrary1
- Create a new web app and add a package reference to our Razor UI class library package
dotnet new razor -o WebApplication2 dotnet add WebApplication2 package ClassLibrary1 --source <current path>/ClassLibrary1/bin/Debug
- Run the new app with the package reference
cd WebApplication2 dotnet run
- Browse to
/test
for the new app to see that your package is getting used.
Publish your package to NuGet to share your handiwork with everyone.
Razor compilation on build
Razor compilation is now part of every build. This means that Razor compilation issues are caught at design time instead of when the app is first run. Compiling the Razor views and pages also significantly speeds up startup time. And even though your view and pages are built up front, you can still modify your Razor files at runtime and see them updated without having to restart the app.
Scaffold identity into an existing project
The latest preview of Visual Studio 2017 (15.7 Preview 3) supports scaffolding identity into an existing application and overriding specific pages from the default identity UI.
To scaffold identity into an existing application:
- Right-click on the project in the solution explorer and select
Add -> New Scaffolded Item...
- Select the Identity scaffolder and click Add.
- The Add Identity dialog appears. Leave the layout unspecified. Check the checkbox in the override file list for “LoginPartial”. Also click the “+” button to create a new data context class and a custom identity user class. Click Add to run the identity scaffolder.The scaffolder will add an Identity area to your application that configures identity and also will update the layout to include the login partial.
- Update the
Configure
method inStartup.cs
to add the database error page when in development and also the authentication middleware before invoking MVC.public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseAuthentication(); app.UseMvc(); }
- The generated
_ViewStart.cshtml
in this preview release contains an unfortunate typo in the specified layout path. Fixup the layout path to be/Pages/Shared/_Layout.cshtml
. This will be fixed in the next release. - Select
Tools -> NuGet Package Manager -> Package Manager Console
and run the following commands to add an EF migration and create the database.Add-Migration Initial Update-Database
- Build and run the application. You should now be able to register and login users.
Customize default Identity UI
The identity scaffolder can also scaffold individual pages to override the default identity UI. For example, you can use a custom user type and update the identity UI to add additional user properties.
- In the solution explorer right-click on the project you added Identity to in the previous section and select
Add -> New Scaffolded Item...
- Select the Identity scaffolder and click Add.
- The Add Identity dialog appears. Again leave the layout unspecified. Check the checkbox for the
Account\Manage\Index
file. For the data context select the data context we created in the previous section. Click Add to run the identity scaffolder. - Fixup the layout path in
_ViewStart.cshtml
as we did previously. - Open the generated
/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
file and replace referencesIdentityUser
with your custom user type (ScaffoldIdentityWebAppUser
). This manual edit is necessary in this preview, but will be handled by the identity scaffolder in a future update. - Update
ScaffoldIdentityWebAppUser
to add anAge
property.public class ScaffoldIdentityWebAppUser : IdentityUser { public int Age { get; set; } }
- Update the
InputModel
in/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
to add a newAge
property.public class InputModel { [Required] [EmailAddress] public string Email { get; set; } [Phone] [Display(Name = "Phone number")] public string PhoneNumber { get; set; } [Range(0, 120)] public int Age { get; set; } }
- Update
/Areas/Identity/Pages/Account/Manage/Index.cshtml
to add a field for setting the Age property. You can the field below the existing phone number field.<div class="form-group"> <label asp-for="Input.Age"></label> <input asp-for="Input.Age" class="form-control" /> <span asp-validation-for="Input.Age" class="text-danger"></span> </div>
- Update the
OnPostAsync
method in/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
to save the user’s age to the database:if (Input.Age >= 0 && Input.Age < 120) { user.Age = Input.Age; await _userManager.UpdateAsync(user); }
- Update the
OnGetAsync
method in to initialize theInputModel
with the user’s age from the database:Input = new InputModel { Email = user.Email, PhoneNumber = user.PhoneNumber, Age = user.Age };
- Select
Tools -> NuGet Package Manager -> Package Manager Console
and run the following commands to add an EF migration and update the database.Add-Migration UserAge Update-Database
- Build and run the app. Register a user and then set the user’s age on the manage page:
Improvements to [ApiController] parameter binding
Applying [ApiController]
to your controller sets up convenient conventions for how parameters get bound to request data. In Preview 2 we’ve made a number of improvements to how these conventions work based on feedback:
[FromBody]
will no longer be inferred for complex types with specific semantics, likeCancellationToken
- Multiple
[FromBody]
parameters will result in an exception - When there are multiple routes for an action parameters that match any route value will be considered
[FromRoute]
Provide constructed model type to the partial tag helper
The partial tag helper now supports passing a model instance through the new model
attribute.
<partial name="MovieView" model='new Movie() { Name="Ghostsmashers" }' />
The asp-for
attribute was also renamed to for
.
Analyzer to warn about using Html.Partial usage
Starting in this preview, calls to Html.Partial
will result in an analyzer warning due to the potential for deadlocks.
Calls to @Html.Partial(...)
should be replaced by @await Html.PartialAsync(...)
or use the partial tag helper instead (<partial name="..." />
).
Option to opt-out of HTTPS
HTTPS is enabled by default in ASP.NET Core 2.1 and the out of the box templates include support for handling HTTPS redirects and HSTS. But in some backend services where HTTPS is being handled externally at the edge using HTTPS at each node is not needed.
In Preview 2 you can disable HTTPS when creating new ASP.NET Core projects by passing the --no-https
option at the command-line. In Visual Studio this option is surfaced from the new ASP.NET Core Web Application dialog.
Razor Pages handling of HEAD requests
Razor Pages will now fall back to calling a matching GET page handler if no HEAD page handler is defined.
Updated to Json.NET 11
We’ve updated to Json.NET 11 to benefit from the latest Json.NET features and improvements.
Added Web API Client to ASP.NET Core meta-packages
The Web API Client is now included by the ASP.NET Core meta-packages for your convenience. This package provides convenient methods for handling formatting and deserialization when calling web APIs.
ViewData backed properties
Properties decorated with [ViewData]
on controllers, page models, and Razor Pages provide a convenient way to add data that can be read by views and pages.
For example, to specify the title for a page and have it show up in the page layout you can define a property on your page model that is decorated with [ViewData]
:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
}
The title can be accessed from the about page as a model property:
@page
@model AboutModel
<h2>@Model.Title</h2>
Then, in the layout, the title can be read from the ViewData
dictionary:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - WebApplication2</title>
...
Prebuilt UI libraries for Azure AD and Azure AD B2C
The UI and components required for setting up authentication with Azure AD or Azure AD B2C are now available in this preview as prebuilt packages:
These packages can be used to setup authentication with Azure AD or Azure AD B2C in any project.
Updates to launchSettings.json
The applicationUrl
property in launchSettings.json
can now be used to specify a semicolon separated list of server URLs.
"WebApplication1": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
Deprecating aspnetcore and aspnetcore-build Docker images
Starting with .NET Core 2.1.0-preview2, we intend to migrate from using the microsoft/aspnetcore-build
and microsoft/aspnetcore
Docker repos to the microsoft/dotnet
Docker repo. We will continue to ship patches and security fixes for the existing aspnetcore images but any new images for 2.1 and higher will be pushed to microsoft/dotnet
.
Dockerfiles using microsoft/aspnetcore: should change to microsoft/dotnet:-aspnetcore-runtime.
Dockerfiles using microsoft/aspnetcore-build that do not require Node should change to microsoft/dotnet:-sdk.
Dockerfiles using microsoft/aspnetcore-build that require Node will need to handle that in their own images, either with a multi-stage build or by installing Node themselves.
For more details on the change, including some example Dockerfiles and a link to a discussion issue, you can see the announcement here: https://github.com/aspnet/Announcements/issues/298
Kestrel support for SNI
Server Name Indication (SNI) can be used to allow hosting multiple domains on the same IP address and port. It does this by sending the expected host name in the TLS handshake so that the server can provide the correct certificate. Kestrel now supports this via the ServerCertificateSelector callback. This is invoked once per connection to allow you to inspect the host name and select the most appropriate certificate.
WebHost.CreateDefaultBuilder() .UseKestrel((context, options) => { options.ListenAnyIP(5005, listenOptions => { listenOptions.UseHttps(httpsOptions => { var localhostCert = CertificateLoader.LoadFromStoreCert("localhost", "My", StoreLocation.CurrentUser, allowInvalid: true); var exampleCert = CertificateLoader.LoadFromStoreCert("example.com", "My", StoreLocation.CurrentUser, allowInvalid: true); var subExampleCert = CertificateLoader.LoadFromStoreCert("sub.example.com", "My", StoreLocation.CurrentUser, allowInvalid: true); var certs = new Dictionary(StringComparer.OrdinalIgnoreCase); certs["localhost"] = localhostCert; certs["example.com"] = exampleCert; certs["sub.example.com"] = subExampleCert; httpsOptions.ServerCertificateSelector = (features, name) => { if (name != null && certs.TryGetValue(name, out var cert)) { return cert; } return exampleCert; }; }); }); });
SNI support requires running on netcoreapp2.1. On netcoreapp2.0 and net461 the callback will be invoked but the name will always be null. The name will also be null if the client did not provide this optional parameter.
HTTTPClient Factory and Polly
As we discussed in our Preview1 post on HttpClient factory, we had planned to provide a package that integrates Polly with HTTPClient factory. In Preview2 the majority of that integrations is now available. In order to try this out you need to add the Polly integration NuGet package Microsoft.Extensions.Http.Polly:
<ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview2-final" /> <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0-preview2-final" /> </ItemGroup>
Then you can write code like the following:
services.AddHttpClient(client => client.BaseAddress = new Uri(Configuration["ValuesServiceUri"])) .AddTransientHttpErrorPolicy(policyBuilder => policyBuilder.Retry(2));
With this code we would automatically retry twice before failing any requests made using the ValuesClient, the ValuesClient here being the same as the one we showed in the Preview1 post. For more information on Polly and what policies are available, you can read the Polly docs here
The Polly.PolicyBuilder provided to AddTransientHttpErrorPolicy has been preconfigured to handle errors in the following categories:
- Network failures (System.Net.Http.HttpRequestException)
- HTTP 5XX status codes (server errors)
- HTTP 408 status code (request timeout)
You can use the more general AddPolicyHandler method to add a Policy that handles different conditions. In general AddTransientHttpErrorPolicy is good for reactive policies (Retry, CircuitBreaker, Fallback), which we think are likely to be the most commonly used. However, the AddPollyHandler API can be used to add any Polly policy handling any conditions that you want.
The policy will be cached indefinitely per named client, which allows policies such as CircuitBreaker to function.
If you want to share a single Policy across multiple named clients then you should create a Policy and then pass it into multiple calls, for example:
var retryPolicy = Policy.Handle() .OrResult(message => !message.IsSuccessStatusCode) .WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) }); services.AddHttpClient() .AddPolicyHandler(retryPolicy); services.AddHttpClient("otherClient") .AddPolicyHandler(retryPolicy);
With this code we will Retry all HttpRequestExceptions and non success status codes 3 times, with a slightly longer pause between each try. We could change to an exponential backoff retry policy by changing the code slightly:
var retryPolicy = Policy.Handle() .OrResult(message => !message.IsSuccessStatusCode) .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) );
For documentation on Polly and the policies available you can read the Polly wiki.
Migrating an ASP.NET Core 2.0.x project to 2.1.0-preview2
Follow these steps to migrate an existing ASP.NET Core 2.0.x project to 2.1.0-preview2:
- Open the project’s CSPROJ file and change the value of the
<TargetFramework>
element tonetcoreapp2.1
- Projects targeting .NET Framework rather than .NET Core, e.g.
net471
, don’t need to do this
- Projects targeting .NET Framework rather than .NET Core, e.g.
- In the same file, update the versions of the various
<PackageReference>
elements for anyMicrosoft.AspNetCore
,Microsoft.Extensions
, andMicrosoft.EntityFrameworkCore
packages to2.1.0-preview2-final
- In the same file, remove any references to
<DotNetCliToolReference>
elements for anyMicrosoft.AspNetCore
,Microsoft.VisualStudio
, andMicrosoft.EntityFrameworkCore
packages. These tools are now deprecated and are replaced by global tools. - In the same file, remove the
<DotNetCliToolReference>
elements for anyMicrosoft.AspNetCore
packages. These have been replaced by global tools.
That should be enough to get the project building and running against 2.1.0-preview2. The following steps will change your project to use new code-based idioms that are recommended in 2.1
- Open the
Program.cs
file - Rename the
BuildWebHost
method toCreateWebHostBuilder
, change its return type toIWebHostBuilder
, and remove the call to.Build()
in its body - Update the call in
Main
to call the renamedCreateWebHostBuilder
method like so:CreateWebHostBuilder(args).Build().Run();
- Open the
Startup.cs
file - In the
ConfigureServices
method, change the call to add MVC services to set the compatibility version to 2.1 like so:services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
- In the
Configure
method, add a call to add the HSTS middleware after the exception handler middleware:app.UseHsts();
- Staying in the
Configure
method, add a call to add the HTTPS redirection middleware before the static files middleware:app.UseHttpsRedirection();
- Open the project propery pages (right-mouse click on project in Visual Studio Solution Explorer and select “Properties”)
- Open the “Debug” tab and in the IIS Express profile, check the “Enable SSL” checkbox and save the changes
Note that some projects might require more steps depending on the options selected when the project was created, or packages added since. You might like to try creating a new project targeting 2.1.0-preview2 (in Visual Studio or using dotnet new
at the cmd line) with the same options to see what other things have changed.