April 12th, 2018

ASP.NET Core 2.1.0-preview2 now available

Damian Edwards
Principal Architect

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:

  1. Create a .NET Standard class library
    dotnet new classlib -o ClassLibrary1
    
  2. Add a reference from the class library to Microsoft.AspNetCore.Mvc
    dotnet add ClassLibrary1 package Microsoft.AspNetCore.Mvc -v 2.1.0-preview2-final
    
  3. Open ClassLibrary1.csproj and change the SDK to be Microsoft.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>
  4. 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
    
  5. Update the Razor page to add some markup
    @page
    
    <h1>Hello from a Razor UI class library!</h1>
  6. Build the class library to ensure there are no build errors
    dotnet build ClassLibrary1
    

    In the build output you should see both ClassLibrary1.dll and ClassLibrary1.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.

  1. Create a ASP.NET Core Web Application
    dotnet new razor -o WebApplication1
    
  2. Create a solution file and add both projects to the solution
    dotnet new sln
    dotnet sln add WebApplication1
    dotnet sln add ClassLibrary1
    
  3. Add a reference from the web application to the class library
    dotnet add WebApplication1 reference ClassLibrary1
    
  4. Build and run the web app
    cd WebApplication1
    dotnet run
    
  5. Browse to /test to see your page from your Razor UI class libraryRazor UI class library

Looks great! Now we can package up our Razor UI class library and share it with others.

  1. Create a package for the Razor UI class library
    cd ..
    dotnet pack ClassLibrary1
    
  2. 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
    
  3. Run the new app with the package reference
    cd WebApplication2
    dotnet run
    
  4. Browse to /test for the new app to see that your package is getting used.Razor UI class library package

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:

  1. Right-click on the project in the solution explorer and select Add -> New Scaffolded Item...
  2. Select the Identity scaffolder and click Add.Add Scaffold Identity
  3. 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.Add default IdentityThe scaffolder will add an Identity area to your application that configures identity and also will update the layout to include the login partial.
  4. Update the Configure method in Startup.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();
    }
  5. 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.
  6. 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
    
  7. Build and run the application. You should now be able to register and login users.Web app login

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.

  1. In the solution explorer right-click on the project you added Identity to in the previous section and select Add -> New Scaffolded Item...
  2. Select the Identity scaffolder and click Add.Add Scaffold Identity
  3. The Add Identity dialog appears. Again leave the layout unspecified. Check the checkbox for the AccountManageIndex file. For the data context select the data context we created in the previous section. Click Add to run the identity scaffolder.Override Manage
  4. Fixup the layout path in _ViewStart.cshtml as we did previously.
  5. Open the generated /Areas/Identity/Pages/Account/Manage/Index.cshtml.cs file and replace references IdentityUser 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.
  6. Update ScaffoldIdentityWebAppUser to add an Age property.
        public class ScaffoldIdentityWebAppUser : IdentityUser
        {
            public int Age { get; set; }
        }
  7. Update the InputModel in /Areas/Identity/Pages/Account/Manage/Index.cshtml.cs to add a new Age 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; }
    }
  8. 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>
  9. 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);
    }
  10. Update the OnGetAsync method in to initialize the InputModel with the user’s age from the database:
    Input = new InputModel
    {
        Email = user.Email,
        PhoneNumber = user.PhoneNumber,
        Age = user.Age
    };
  11. 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
    
  12. Build and run the app. Register a user and then set the user’s age on the manage page:Web app manage

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, like CancellationToken
  • 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.

Html.Partial warning

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.

Disable HTTPS

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:<version> should change to microsoft/dotnet:<version>-aspnetcore-runtime.

Dockerfiles using microsoft/aspnetcore-build:<version> that do not require Node should change to microsoft/dotnet:<version>-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.

HTTPClient 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 integration 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.RetryAsync(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<HttpRequestException>()
                        .OrResult<HttpResponseMessage>(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<HttpRequestException>()
                        .OrResult<HttpResponseMessage>(message => !message.IsSuccessStatusCode)
                        .WaitAndRetryAsync(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:

  1. Open the project’s CSPROJ file and change the value of the <TargetFramework> element to netcoreapp2.1
    • Projects targeting .NET Framework rather than .NET Core, e.g. net471, don’t need to do this
  2. In the same file, update the versions of the various <PackageReference> elements for any Microsoft.AspNetCore, Microsoft.Extensions, and Microsoft.EntityFrameworkCore packages to 2.1.0-preview2-final
  3. In the same file, remove any references to <DotNetCliToolReference> elements for any Microsoft.AspNetCore, Microsoft.VisualStudio, and Microsoft.EntityFrameworkCore packages. These tools are now deprecated and are 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

  1. Open the Program.cs file
  2. Rename the BuildWebHost method to CreateWebHostBuilder, change its return type to IWebHostBuilder, and remove the call to .Build() in its body
  3. Update the call in Main to call the renamed CreateWebHostBuilder method like so: CreateWebHostBuilder(args).Build().Run();
  4. Open the Startup.cs file
  5. 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);
  6. In the Configure method, add a call to add the HSTS middleware after the exception handler middleware: app.UseHsts();
  7. Staying in the Configure method, add a call to add the HTTPS redirection middleware before the static files middleware: app.UseHttpsRedirection();
  8. Open the project propery pages (right-mouse click on project in Visual Studio Solution Explorer and select “Properties”)
  9. 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.

    Author

    Damian Edwards
    Principal Architect

    Damian's a Principal Architect on the .NET product team at Microsoft.

    0 comments

    Discussion are closed.