Posted on Leave a comment

ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 9

Daniel Roth

Daniel

.NET Core 3.0 Preview 9 is now available and it contains a number of improvements and updates to ASP.NET Core and Blazor.

Here’s the list of what’s new in this preview:

  • Blazor event handlers and data binding attributes moved to Microsoft.AspNetCore.Components.Web
  • Blazor routing improvements
    • Render content using a specific layout
    • Routing decoupled from authorization
    • Route to components from multiple assemblies
  • Render multiple Blazor components from MVC views or pages
  • Smarter reconnection for Blazor Server apps
  • Utility base component classes for managing a dependency injection scope
  • Razor component unit test framework prototype
  • Helper methods for returning Problem Details from controllers
  • New client API for gRPC
  • Support for async streams in streaming gRPC responses

Please see the release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET Core 3.0 Preview 9 install the .NET Core 3.0 Preview 9 SDK.

If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2019.

.NET Core 3.0 Preview 9 requires Visual Studio 2019 16.3 Preview 3 or later.

To install the latest Blazor WebAssembly template also run the following command:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview9.19424.4

Upgrade an existing project

To upgrade an existing ASP.NET Core app to .NET Core 3.0 Preview 9, follow the migrations steps in the ASP.NET Core docs.

Please also see the full list of breaking changes in ASP.NET Core 3.0.

To upgrade an existing ASP.NET Core 3.0 Preview 8 project to Preview 9:

  • Update all Microsoft.AspNetCore.* package references to 3.0.0-preview9.19424.4
  • In Blazor apps and libraries:
    • Add a using statement for Microsoft.AspNetCore.Components.Web in your top level _Imports.razor file (see Blazor event handlers and data binding attributes moved to Microsoft.AspNetCore.Components.Web below for details)
    • Add a using statement for Microsoft.AspNetCore.Components.Authorization in your top level _Imports.razor file.
    • Update all Blazor component parameters to be public.
    • Update implementations of IJSRuntime to return ValueTask<T>.
    • Replace calls to MapBlazorHub<TComponent> with a single call to MapBlazorHub.
    • Update calls to RenderComponentAsync and RenderStaticComponentAsync to use the new overloads to RenderComponentAsync that take a RenderMode parameter (see Render multiple Blazor components from MVC views or pages below for details).
    • Update App.razor to use the updated Router component (see Blazor routing improvements below for details).
    • (Optional) Remove page specific _Imports.razor file with the @layout directive to use the default layout specified through the router instead.
    • Remove any use of the PageDisplay component and replace with LayoutView, RouteView, or AuthorizeRouteView as appropriate (see Blazor routing improvements below for details).
    • Replace uses of IUriHelper with NavigationManager.
    • Remove any use of @ref:suppressField.
    • Replace the previous RevalidatingAuthenticationStateProvider code with the new RevalidatingIdentityAuthenticationStateProvider code from the project template.
    • Replace Microsoft.AspNetCore.Components.UIEventArgs with System.EventArgs and remove the “UI” prefix from all EventArgs derived types (UIChangeEventArgs -> ChangeEventArgs, etc.).
    • Replace DotNetObjectRef with DotNetObjectReference.
    • Replace OnAfterRender() and OnAfterRenderAsync() implementations with OnAfterRender(bool firstRender) or OnAfterRenderAsync(bool firstRender).
  • In gRPC projects:
    • Update calls to GrpcClient.Create with a call GrpcChannel.ForAddress to create a new gRPC channel and new up your typed gRPC clients using this channel.
    • Rebuild any project or project dependency that uses gRPC code generation for an ABI change in which all clients inherit from ClientBase instead of LiteClientBase. There are no code changes required for this change.
    • Please also see the grpc-dotnet announcement for all changes.

You should now be all set to use .NET Core 3.0 Preview 9!

Blazor event handlers and data binding attributes moved to Microsoft.AspNetCore.Components.Web

In this release we moved the set of bindings and event handlers available for HTML elements into the Microsoft.AspNetCore.Components.Web.dll assembly and into the Microsoft.AspNetCore.Components.Web namespace. This change was made to isolate the web specific aspects of the Blazor programming from the core programming model. This section provides additional details on how to upgrade your existing projects to react to this change.

Blazor apps

Open the application’s root _Imports.razor and add @using Microsoft.AspNetCore.Components.Web. Blazor apps get a reference to the Microsoft.AspNetCore.Components.Web package implicitly without any additional package references, so adding a reference to this package isn’t necessary.

Blazor libraries

Add a package reference to the Microsoft.AspNetCore.Components.Web package package if you don’t already have one. Then open the root _Imports.razor file for the project (create the file if you don’t already have it) and add @using Microsoft.AspNetCore.Components.Web.

Troubleshooting guidance

With the correct references and using statement for Microsoft.AspNetCore.Components.Web, event handlers like @onclick and @bind should be bold font and colorized as shown below when using Visual Studio.

Events and binding working in Visual Studio

If @bind or @onclick are colorized as a normal HTML attribute, then the @using statement is missing.

Events and binding not recognized

If you’re missing a using statement for the Microsoft.AspNetCore.Components.Web namespace, you may see build failures. For example, the following build error for the code shown above indicates that the @bind attribute wasn’t recognized:

CS0169 The field 'Index.text' is never used
CS0428 Cannot convert method group 'Submit' to non-delegate type 'object'. Did you intend to invoke the method?

In other cases you may get a runtime exception and the app fails to render. For example, the following runtime exception seen in the browser console indicates that the @onclick attribute wasn’t recognized:

Error: There was an error applying batch 2.
DOMException: Failed to execute 'setAttribute' on 'Element': '@onclick' is not a valid attribute name.

Add a using statement for the Microsoft.AspNetCore.Components.Web namespace to address these issues. If adding the using statement fixed the problem, consider moving to the using statement app’s root _Imports.razor so it will apply to all files.

If you add the Microsoft.AspNetCore.Components.Web namespace but get the following build error, then you’re missing a package reference to the Microsoft.AspNetCore.Components.Web package:

CS0234 The type or namespace name 'Web' does not exist in the namespace 'Microsoft.AspNetCore.Components' (are you missing an assembly reference?)

Add a package reference to the Microsoft.AspNetCore.Components.Web package to address the issue.

Blazor routing improvements

In this release we’ve revised the Blazor Router component to make it more flexible and to enable new scenarios. The Router component in Blazor handles rendering the correct component that matches the current address. Routable components are marked with the @page directive, which adds the RouteAttribute to the generated component classes. If the current address matches a route, then the Router renders the contents of its Found parameter. If no route matches, then the Router component renders the contents of its NotFound parameter.

To render the component with the matched route, use the new RouteView component passing in the supplied RouteData from the Router along with any desired parameters. The RouteView component will render the matched component with its layout if it has one. You can also optionally specify a default layout to use if the matched component doesn’t have one.

<Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" /> </Found> <NotFound> <h1>Page not found</h1> <p>Sorry, but there's nothing here!</p> </NotFound>
</Router>

Render content using a specific layout

To render a component using a particular layout, use the new LayoutView component. This is useful when specifying content for not found pages that you still want to use the app’s layout.

<Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="typeof(MainLayout)"> <h1>Page not found</h1> <p>Sorry, but there's nothing here!</p> </LayoutView> </NotFound>
</Router>

Routing decoupled from authorization

Authorization is no longer handled directly by the Router. Instead, you use the AuthorizeRouteView component. The AuthorizeRouteView component is a RouteView that will only render the matched component if the user is authorized. Authorization rules for specific components are specified using the AuthorizeAttribute. The AuthorizeRouteView component also sets up the AuthenticationState as a cascading value if there isn’t one already. Otherwise, you can still manually setup the AuthenticationState as a cascading value using the CascadingAuthenticationState component.

<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <CascadingAuthenticationState> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </CascadingAuthenticationState> </NotFound>
</Router>

You can optionally set the NotAuthorized and Authorizing parameters of the AuthorizedRouteView component to specify content to display if the user is not authorized or authorization is still in progress.

<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <NotAuthorized> <p>Nope, nope!</p> </NotAuthorized> </AuthorizeRouteView> </Found>
</Router>

Route to components from multiple assemblies

You can now specify additional assemblies for the Router component to consider when searching for routable components. These assemblies will be considered in addition to the specified AppAssembly. You specify these assemblies using the AdditionalAssemblies parameter. For example, if Component1 is a routable component defined in a referenced class library, then you can support routing to this component like this:

<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Component1).Assembly }> ...
</Router>

Render multiple Blazor components from MVC views or pages

We’ve reenabled support for rendering multiple components from a view or page in a Blazor Server app. To render a component from a .cshtml file, use the Html.RenderComponentAsync<TComponent>(RenderMode renderMode, object parameters) HTML helper method with the desired RenderMode.

RenderMode Description Supports parameters?
Static Statically render the component with the specified parameters. Yes
Server Render a marker where the component should be rendered interactively by the Blazor Server app. No
ServerPrerendered Statically prerender the component along with a marker to indicate the component should later be rendered interactively by the Blazor Server app. No

Support for stateful prerendering has been removed in this release due to security concerns. You can no longer prerender components and then connect back to the same component state when the app loads. We may reenable this feature in a future release post .NET Core 3.0.

Blazor Server apps also no longer require that the entry point components be registered in the app’s Configure method. Only a single call to MapBlazorHub() is required.

Smarter reconnection for Blazor Server apps

Blazor Server apps are stateful and require an active connection to the server in order to function. If the network connection is lost, the app will try to reconnect to the server. If the connection can be reestablished but the server state is lost, then reconnection will fail. Blazor Server apps will now detect this condition and recommend the user to refresh the browser instead of retrying to connect.

Blazor Server reconnect rejected

Utility base component classes for managing a dependency injection scope

In ASP.NET Core apps, scoped services are typically scoped to the current request. After the request completes, any scoped or transient services are disposed by the dependency injection (DI) system. In Blazor Server apps, the request scope lasts for the duration of the client connection, which can result in transient and scoped services living much longer than expected.

To scope services to the lifetime of a component you can use the new OwningComponentBase and OwningComponentBase<TService> base classes. These base classes expose a ScopedServices property of type IServiceProvider that can be used to resolve services that are scoped to the lifetime of the component. To author a component that inherits from a base class in Razor use the @inherits directive.

@page "/users"
@attribute [Authorize]
@inherits OwningComponentBase<Data.ApplicationDbContext> <h1>Users (@Service.Users.Count())</h1>
<ul> @foreach (var user in Service.Users) { <li>@user.UserName</li> }
</ul>

Note: Services injected into the component using @inject or the InjectAttribute are not created in the component’s scope and will still be tied to the request scope.

Razor component unit test framework prototype

We’ve started experimenting with building a unit test framework for Razor components. You can read about the prototype in Steve Sanderson’s Unit testing Blazor components – a prototype blog post. While this work won’t ship with .NET Core 3.0, we’d still love to get your feedback early in the design process. Take a look at the code on GitHub and let us know what you think!

Helper methods for returning Problem Details from controllers

Problem Details is a standardized format for returning error information from an HTTP endpoint. We’ve added new Problem and ValidationProblem method overloads to controllers that use optional parameters to simplify returning Problem Detail responses.

[Route("/error")]
public ActionResult<ProblemDetails> HandleError()
{ return Problem(title: "An error occurred while processing your request", statusCode: 500);
}

New client API for gRPC

To improve compatibility with the existing Grpc.Core implementation, we’ve changed our client API to use gRPC channels. The channel is where gRPC configuration is set and it is used to create strongly typed clients. The new API provides a more consistent client experience with Grpc.Core, making it easier to switch between using the two libraries.

// Old
using var httpClient = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
var client = GrpcClient.Create<GreeterClient>(httpClient); // New
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new GreeterClient(channel); var reply = await client.GreetAsync(new HelloRequest { Name = "Santa" });

Support for async streams in streaming gRPC responses

gRPC streaming responses return a custom IAsyncStreamReader type that can be iterated on to receive all response messages in a streaming response. With the addition of async streams in C# 8, we’ve added a new extension method that makes for a more ergonomic API while consuming streaming responses.

// Old
while (await requestStream.MoveNext(CancellationToken.None))
{ var message = requestStream.Current; // …
} // New and improved
await foreach (var message in requestStream.ReadAllAsync())
{ // …
}

Give feedback

We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core and Blazor!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

Redesigning Configuration Refresh for Azure App Configuration

Avatar

Overview

Since its inception, the .NET Core configuration provider for Azure App Configuration has provided the capability to monitor changes and sync them to the configuration within a running application. We recently redesigned this functionality to allow for on-demand refresh of the configuration. The new design paves the way for smarter applications that only refresh the configuration when necessary. As a result, inactive applications no longer have to monitor for configuration changes unnecessarily.
 

Initial design : Timer-based watch

In the initial design, configuration was kept in sync with Azure App Configuration using a watch mechanism which ran on a timer. At the time of initialization of the Azure App Configuration provider, users could specify the configuration settings to be updated and an optional polling interval. In case the polling interval was not specified, a default value of 30 seconds was used.

public static IWebHost BuildWebHost(string[] args)
{ WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // Load settings from Azure App Configuration // Set up the provider to listen for changes triggered by a sentinel value var settings = config.Build(); string appConfigurationEndpoint = settings["AzureAppConfigurationEndpoint"]; config.AddAzureAppConfiguration(options => { options.ConnectWithManagedIdentity(appConfigurationEndpoint) .Use(keyFilter: "WebDemo:*") .WatchAndReloadAll(key: "WebDemo:Sentinel", label: LabelFilter.Null); }); settings = config.Build(); }) .UseStartup<Startup>() .Build();
}

For example, in the above code snippet, Azure App Configuration would be pinged every 30 seconds for changes. These calls would be made irrespective of whether the application was active or not. As a result, there would be unnecessary usage of network and CPU resources within inactive applications. Applications needed a way to trigger a refresh of the configuration on demand in order to be able to limit the refreshes to active applications. Then unnecessary checks for changes could be avoided.

This timer-based watch mechanism had the following fundamental design flaws.

  1. It could not be invoked on-demand.
  2. It continued to run in the background even in applications that could be considered inactive.
  3. It promoted constant polling of configuration rather than a more intelligent approach of updating configuration when applications are active or need to ensure freshness.
     

New design : Activity-based refresh

The new refresh mechanism allows users to keep their configuration updated using a middleware to determine activity. As long as the ASP.NET Core web application continues to receive requests, the configuration settings continue to get updated with the configuration store.

The application can be configured to trigger refresh for each request by adding the Azure App Configuration middleware from package Microsoft.Azure.AppConfiguration.AspNetCore in your application’s startup code.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAzureAppConfiguration(); app.UseMvc();
}

At the time of initialization of the configuration provider, the user can use the ConfigureRefresh method to register the configuration settings to be updated with an optional cache expiration time. In case the cache expiration time is not specified, a default value of 30 seconds is used.

public static IWebHost BuildWebHost(string[] args)
{ WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // Load settings from Azure App Configuration // Set up the provider to listen for changes triggered by a sentinel value var settings = config.Build(); string appConfigurationEndpoint = settings["AzureAppConfigurationEndpoint"]; config.AddAzureAppConfiguration(options => { options.ConnectWithManagedIdentity(appConfigurationEndpoint) .Use(keyFilter: "WebDemo:*") .ConfigureRefresh((refreshOptions) => { // Indicates that all settings should be refreshed when the given key has changed refreshOptions.Register(key: "WebDemo:Sentinel", label: LabelFilter.Null, refreshAll: true); }); }); settings = config.Build(); }) .UseStartup<Startup>() .Build();
}

In order to keep the settings updated and avoid unnecessary calls to the configuration store, an internal cache is used for each setting. Until the cached value of a setting has expired, the refresh operation does not update the value. This happens even when the value has changed in the configuration store.  

Try it now!

For more information about Azure App Configuration, check out the following resources. You can find step-by-step tutorials that would help you get started with dynamic configuration using the new refresh mechanism within minutes. Please let us know what you think by filing issues on GitHub.

Overview: Azure App configuration
Tutorial: Use dynamic configuration in an ASP.NET Core app
Tutorial: Use dynamic configuration in a .NET Core app
Related Blog: Configuring a Server-side Blazor app with Azure App Configuration

Avatar

Software Engineer, Azure App Configuration

Follow    

Posted on Leave a comment

ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 8

Avatar

Sourabh

.NET Core 3.0 Preview 8 is now available and it includes a bunch of new updates to ASP.NET Core and Blazor.

Here’s the list of what’s new in this preview:

  • Project template updates
    • Cleaned up top-level templates in Visual Studio
    • Angular template updated to Angular 8
    • Blazor templates renamed and simplified
    • Razor Class Library template replaces the Blazor Class Library template
  • Case-sensitive component binding
  • Improved reconnection logic for Blazor Server apps
  • NavLink component updated to handle additional attributes
  • Culture aware data binding
  • Automatic generation of backing fields for @ref
  • Razor Pages support for @attribute
  • New networking primitives for non-HTTP Servers
  • Unix domain socket support for the Kestrel Sockets transport
  • gRPC support for CallCredentials
  • ServiceReference tooling in Visual Studio
  • Diagnostics improvements for gRPC

Please see the release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET Core 3.0 Preview 8 install the .NET Core 3.0 Preview 8 SDK

If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2019.

.NET Core 3.0 Preview 8 requires Visual Studio 2019 16.3 Preview 2 or later

To install the latest Blazor WebAssembly template also run the following command:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview8.19405.7

Upgrade an existing project

To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 8, follow the migrations steps in the ASP.NET Core docs.

Please also see the full list of breaking changes in ASP.NET Core 3.0.

To upgrade an existing ASP.NET Core 3.0 Preview 7 project to Preview 8:

  • Update Microsoft.AspNetCore.* package references to 3.0.0-preview8.19405.7.
  • In Razor components rename OnInit to OnInitialized and OnInitAsync to OnInitializedAsync.
  • In Blazor apps, update Razor component parameters to be public, as non-public component parameters now result in an error.
  • In Blazor WebAssembly apps that make use of the HttpClient JSON helpers, add a package reference to Microsoft.AspNetCore.Blazor.HttpClient.
  • On Blazor form components remove use of Id and Class parameters and instead use the HTML id and class attributes.
  • Rename ElementRef to ElementReference.
  • Remove backing field declarations when using @ref or specify the @ref:suppressField parameter to suppress automatic backing field generation.
  • Update calls to ComponentBase.Invoke to call ComponentBase.InvokeAsync.
  • Update uses of ParameterCollection to use ParameterView.
  • Update uses of IComponent.Configure to use IComponent.Attach.
  • Remove use of namespace Microsoft.AspNetCore.Components.Layouts.

You should hopefully now be all set to use .NET Core 3.0 Preview 8.

Project template updates

Cleaned up top-level templates in Visual Studio

Top level ASP.NET Core project templates in the “Create a new project” dialog in Visual Studio no longer appear duplicated in the “Create a new ASP.NET Core web application” dialog. The following ASP.NET Core templates now only appear in the “Create a new project” dialog:

  • Razor Class Library
  • Blazor App
  • Worker Service
  • gRPC Service

Angular template updated to Angular 8

The Angular template for ASP.NET Core 3.0 has now been updated to use Angular 8.

Blazor templates renamed and simplified

We’ve updated the Blazor templates to use a consistent naming style and to simplify the number of templates:

  • The “Blazor (server-side)” template is now called “Blazor Server App”. Use blazorserver to create a Blazor Server app from the command-line.
  • The “Blazor” template is now called “Blazor WebAssembly App”. Use blazorwasm to create a Blazor WebAssembly app from the command-line.
  • To create an ASP.NET Core hosted Blazor WebAssembly app, select the “ASP.NET Core hosted” option in Visual Studio, or pass the --hosted on the command-line

Create a new Blazor app

dotnet new blazorwasm --hosted

Razor Class Library template replaces the Blazor Class Library template

The Razor Class Library template is now setup for Razor component development by default and the Blazor Class Library template has been removed. New Razor Class Library projects target .NET Standard so they can be used from both Blazor Server and Blazor WebAssembly apps. To create a new Razor Class Library template that targets .NET Core and supports Pages and Views instead, select the “Support pages and views” option in Visual Studio, or pass the --support-pages-and-views option on the command-line.

Razor Class Library for Pages and Views

dotnet new razorclasslib --support-pages-and-views

Case-sensitive component binding

Components in .razor files are now case-sensitive. This enables some useful new scenarios and improves diagnostics from the Razor compiler.

For example, the Counter has a button for incrementing the count that is styled as a primary button. What if we wanted a Button component that is styled as a primary button by default? Creating a component named Button in previous Blazor releases was problematic because it clashed with the button HTML element, but now that component matching is case-sensitive we can create our Button component and use it in Counter without issue.

Button.razor

<button class="btn btn-primary" @attributes="AdditionalAttributes" @onclick="OnClick">@ChildContent</button> @code { [Parameter] public EventCallback<UIMouseEventArgs> OnClick { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Counter.razor

@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <Button OnClick="IncrementCount">Click me</Button> @code { int currentCount = 0; void IncrementCount() { currentCount++; }
}

Notice that the Button component is pascal cased, which is the typical style for .NET types. If we instead try to name our component button we get a warning that components cannot start with a lowercase letter due to the potential conflicts with HTML elements.

Lowercase component warning

We can move the Button component into a Razor Class Library so that it can be reused in other projects. We can then reference the Razor Class Library from our web app. The Button component will now have the default namespace of the Razor Class Library. The Razor compiler will resolve components based on the in scope namespaces. If we try to use our Button component without adding a using statement for the requisite namespace, we now get a useful error message at build time.

Unrecognized component error

NavLink component updated to handle additional attributes

The built-in NavLink component now supports passing through additional attributes to the rendered anchor tag. Previously NavLink had specific support for the href and class attributes, but now you can specify any additional attribute you’d like. For example, you can specify the anchor target like this:

<NavLink href="my-page" target="_blank">My page</NavLink>

which would render:

<a href="my-page" target="_blank" rel="noopener noreferrer">My page</a>

Improved reconnection logic for Blazor Server apps

Blazor Server apps require a live connection to the server in order to function. If the connection or the server-side state associated with it is lost, then the the client will be unable to function. Blazor Server apps will attempt to reconnect to the server in the event of an intermittent connection loss and this logic has been made more robust in this release. If the reconnection attempts fail before the network connection can be reestablished, then the user can still attempt to retry the connection manually by clicking the provided “Retry” button.

However, if the server-side state associated with the connect was also lost (e.g. the server was restarted) then clients will still be unable to connect. A common situation where this occurs is during development in Visual Studio. Visual Studio will watch the project for file changes and then rebuild and restart the app as changes occur. When this happens the server-side state associated with any connected clients is lost, so any attempt to reconnect with that state will fail. The only option is to reload the app and establish a new connection.

New in this release, the app will now also suggest that the user reload the browser when the connection is lost and reconnection fails.

Reload prompt

Culture aware data binding

Data-binding support (@bind) for <input> elements is now culture-aware. Data bound values will be formatted for display and parsed using the current culture as specified by the System.Globalization.CultureInfo.CurrentCulture property. This means that @bind will work correctly when the user’s desired culture has been set as the current culture, which is typically done using the ASP.NET Core localization middleware (see Localization).

You can also manually specify the culture to use for data binding using the new @bind:culture parameter, where the value of the parameter is a CultureInfo instance. For example, to bind using the invariant culture:

<input @bind="amount" @bind:culture="CultureInfo.InvariantCulture" />

The <input type="number" /> and <input type="date" /> field types will by default use CultureInfo.InvariantCulture and the formatting rules appropriate for these field types in the browser. These field types cannot contain free-form text and have a look and feel that is controller by the browser.

Other field types with specific formatting requirements include datetime-local, month, and week. These field types are not supported by Blazor at the time of writing because they are not supported by all major browsers.

Data binding now also includes support for binding to DateTime?, DateTimeOffset, and DateTimeOffset?.

Automatic generation of backing fields for @ref

The Razor compiler will now automatically generate a backing field for both element and component references when using @ref. You no longer need to define these fields manually:

<button @ref="myButton" @onclick="OnClicked">Click me</button> <Counter @ref="myCounter" IncrementAmount="10" /> @code { void OnClicked() => Console.WriteLine($"I have a {myButton} and myCounter.IncrementAmount={myCounter.IncrementAmount}");
}

In some cases you may still want to manually create the backing field. For example, declaring the backing field manually is required when referencing generic components. To suppress backing field generation specify the @ref:suppressField parameter.

Razor Pages support for @attribute

Razor Pages now support the new @attribute directive for adding attributes to the generate page class.

For example, you can now specify that a page requires authorization like this:

@page
@attribute [Microsoft.AspNetCore.Authorization.Authorize] <h1>Authorized users only!<h1> <p>Hello @User.Identity.Name. You are authorized!</p>

New networking primitives for non-HTTP Servers

As part of the effort to decouple the components of Kestrel, we are introducing new networking primitives allowing you to add support for non-HTTP protocols.

You can bind to an endpoint (System.Net.EndPoint) by calling Bind on an IConnectionListenerFactory. This returns a IConnectionListener which can be used to accept new connections. Calling AcceptAsync returns a ConnectionContext with details on the connection. A ConnectionContext is similar to HttpContext except it represents a connection instead of an HTTP request and response.

The example below show a simple TCP Echo server hosted in a BackgroundService built using these new primitives.

public class TcpEchoServer : BackgroundService
{ private readonly ILogger<TcpEchoServer> _logger; private readonly IConnectionListenerFactory _factory; private IConnectionListener _listener; public TcpEchoServer(ILogger<TcpEchoServer> logger, IConnectionListenerFactory factory) { _logger = logger; _factory = factory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _listener = await _factory.BindAsync(new IPEndPoint(IPAddress.Loopback, 6000), stoppingToken); while (true) { var connection = await _listener.AcceptAsync(stoppingToken); // AcceptAsync will return null upon disposing the listener if (connection == null) { break; } // In an actual server, ensure all accepted connections are disposed prior to completing _ = Echo(connection, stoppingToken); } } public override async Task StopAsync(CancellationToken cancellationToken) { await _listener.DisposeAsync(); } private async Task Echo(ConnectionContext connection, CancellationToken stoppingToken) { try { var input = connection.Transport.Input; var output = connection.Transport.Output; await input.CopyToAsync(output, stoppingToken); } catch (OperationCanceledException) { _logger.LogInformation("Connection {ConnectionId} cancelled due to server shutdown", connection.ConnectionId); } catch (Exception e) { _logger.LogError(e, "Connection {ConnectionId} threw an exception", connection.ConnectionId); } finally { await connection.DisposeAsync(); _logger.LogInformation("Connection {ConnectionId} disconnected", connection.ConnectionId); } }
}

Unix domain socket support for the Kestrel Sockets transport

We’ve updated the default sockets transport in Kestrel to add support Unix domain sockets (on Linux, macOS, and Windows 10, version 1803 and newer). To bind to a Unix socket, you can call the ListenUnixSocket() method on KestrelServerOptions.

public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder .ConfigureKestrel(o => { o.ListenUnixSocket("/var/listen.sock"); }) .UseStartup<Startup>(); });

gRPC support for CallCredentials

In preview8, we’ve added support for CallCredentials allowing for interoperability with existing libraries like Grpc.Auth that rely on CallCredentials.

Diagnostics improvements for gRPC

Support for Activity

The gRPC client and server use Activities to annotate inbound/outbound requests with baggage containing information about the current RPC operation. This information can be accessed by telemetry frameworks for distributed tracing and by logging frameworks.

EventCounters

The newly introduced Grpc.AspNetCore.Server and Grpc.Net.Client providers now emit the following event counters:

  • total-calls
  • current-calls
  • calls-failed
  • calls-deadline-exceeded
  • messages-sent
  • messages-received
  • calls-unimplemented

You can use the dotnet counters global tool to view the metrics emitted.

dotnet counters monitor -p <PID> Grpc.AspNetCore.Server

ServiceReference tooling in Visual Studio

We’ve added support in Visual Studio that makes it easier to manage references to other Protocol Buffers documents and Open API documents.

ServiceReference

When pointed at OpenAPI documents, the ServiceReference experience in Visual Studio can generated typed C#/TypeScript clients using NSwag.

When pointed at Protocol Buffer (.proto) files, the ServiceReference experience will Visual Studio can generate gRPC service stubs, gRPC clients, or message types using the Grpc.Tools package.

SignalR User Survey

We’re interested in how you use SignalR and the Azure SignalR Service, and your opinions on SignalR features. To that end, we’ve created a survey we’d like to invite any SignalR customer to complete. If you’re interested in talking to one of the engineers from the SignalR team about your ideas or feedback, we’ve provided an opportunity to enter your contact information in the survey, but that information is not required. Help us plan the next wave of SignalR features by providing your feedback in the survey.

Give feedback

We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core and Blazor!

Avatar

Posted on Leave a comment

HttpRepl: A command-line tool for interacting with RESTful HTTP services

Angelos Petropoulos

Angelos

The ASP.NET team has built a command-line tool called HttpRepl. It lets you browse and invoke HTTP services in a similar way to working with files and folders. You give it a starting point (a base URL) and then you can execute commands like “dir” and “cd” to navigate your way around the API:

C:\> dotnet httprepl http://localhost:65369/
(Disconnected)~ set base http://localhost:65369
Using swagger metadata from http://localhost:65369/swagger/v1/swagger.json http://localhost:65369/~ dir
. []
Fruits [get|post]
People [get|post] http://localhost:65369/~ cd People
/People [get|post] http://localhost:65369/People~ dir
. [get|post]
.. []
{id} [get]

Once you have identified the API you are interested in, you can use all the typical HTTP verbs against it. Here is an example of calling GET on http://localhost:65369/People as a continuation from before:

http://localhost:65369/People~ get
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 24 Jul 2019 20:33:07 GMT
Server: Microsoft-IIS/10.0
Transfer-Encoding: chunked
X-Powered-By: ASP.NET [ { "id": 1, "name": "Scott Hunter" }, { "id": 0, "name": "Scott Hanselman" }, { "id": 2, "name": "Scott Guthrie" }
]

Right now HttpRepl is being shipped as a .NET Core Global Tool, which means all you have to do to get it is run the following command on a machine with the .NET Core SDK installed:

C:\> dotnet tool install -g Microsoft.dotnet-httprepl --version “3.0.0-*”

The ASP.NET team built HttpRepl for the purpose of exploring and testing APIs. The idea was to make the experience of exploring and testing APIs through a command-line more convenient. What do you think about HttpRepl and what other uses do you envision for it? We would love to hear your opinion, please leave us a comment below or visit the project on GitHub. And for those wondering, HttpRepl’s official ship date is expected to align with .NET Core 3.0 GA.

Configure Visual Studio Code to launch HttpRepl on debug

You can configure Visual Studio to launch HttpRepl when debugging (along with your web app) by creating a new launch configuration as follows:

"version": "0.2.0", "compounds": [ { "name": ".NET Core REPL", "configurations": [ ".NET Core Launch (web)", "httprepl" ] } ], "configurations": [ { "name": "httprepl", "type": "coreclr", "request": "launch", "program": "dotnet", "args": ["httprepl", "http://localhost:5000"], "cwd": "${workspaceFolder}", "stopAtEntry": false, "console": "integratedTerminal" }, { "name": ".NET Core Launch (web)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/api.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser "serverReadyAction": { "action": "openExternally", "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" }, "env": { "ASPNETCORE_ENVIRONMENT": "Development" }, "sourceFileMap": { "/Views": "${workspaceFolder}/Views" } }

Configure Visual Studio for Windows to launch HttpRepl on F5

You can configure Visual Studio to automatically launch HttpRepl when you F5 a project with the following simple steps:

The .exe for HttpRepl on Windows can be found in the following location:

%USERPROFILE%\.dotnet\tools\dotnet-httprepl.exe

Don’t forget to select it from the menu after adding it:

Next time you F5 your project, Visual Studio will automatically launch HttpRepl with the appropriate base URL (same URL that would have been passed to a browser, controlled through launchsettings):

Note: We are currently working on integrating HttpRepl into Visual Studio, which will give you an out-of-the box and more refined experience.

Configure Visual Studio for Mac to launch HttpRepl as a Custom Tool

In Visual Studio for Mac, you can configure a Custom Tool to open a new Terminal window and start httprepl. To configure this, go to Tools>Edit Custom Tools…

This will bring you to the External Tools dialog where you can add a new tool. To get started click the Add button to add a new tool. Here you will configure a new tool to launch a new Terminal instance and start the httprepl tool. Fill in the dialog with the following values.

  • Title: dotnet httprepl
  • Command: osascript
  • Arguments: -e ‘tell application “Terminal” to activate’ -e ‘tell application “Terminal”
    to do script “dotnet-httprepl”‘
  • Working directory: ${ProjectDir}

See the image below showing this new tool:

After clicking OK a new tool will appear in the Tools menu. To test your application with httprepl, start your application with Run>Start Debugging (or Run>Start without Debugging) and then start the httprepl with the new tool in the Tools menu:

When you invoke the tool a new Terminal window should appear in the foreground. From here you can set the base url to that of the api that you would like to test with set base. For example, see the image below that shows set base was executed and a get request will be executed next:

Give us feedback

It’s not HTTPie, it’s not Curl, but it’s also not PostMan. It’s something that you run and stays running and its aware of its current context. We find this experience valuable, but ultimately what matters the most is what you think. Please let us know your opinion by leaving comments below or on GitHub.

Angelos Petropoulos

Posted on Leave a comment

ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 7

Daniel Roth

Daniel

.NET Core 3.0 Preview 7 is now available and it includes a bunch of new updates to ASP.NET Core and Blazor.

Here’s the list of what’s new in this preview:

  • Latest Visual Studio preview includes .NET Core 3.0 as the default runtime
  • Top level ASP.NET Core templates in Visual Studio
  • Simplified web templates
  • Attribute splatting for components
  • Data binding support for TypeConverters and generics
  • Clarified which directive attributes expect HTML vs C#
  • EventCounters
  • HTTPS in gRPC templates
  • gRPC Client Improvements
  • gRPC Metapackage
  • CLI tool for managing gRPC code generation

Please see the release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET Core 3.0 Preview 7 install the .NET Core 3.0 Preview 7 SDK

If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2019.

Note: .NET Core 3.0 Preview 7 requires Visual Studio 2019 16.3 Preview 1, which is being released later this week.

To install the latest client-side Blazor templates also run the following command:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview7.19365.7

Installing the Blazor Visual Studio extension is no longer required and it can be uninstalled if you’ve installed a previous version. Installing the Blazor WebAssembly templates from the command-line is now all you need to do to get them to show up in Visual Studio.

Upgrade an existing project

To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 7, follow the migrations steps in the ASP.NET Core docs.

Please also see the full list of breaking changes in ASP.NET Core 3.0.

To upgrade an existing ASP.NET Core 3.0 Preview 6 project to Preview 7:

  • Update Microsoft.AspNetCore.* package references to 3.0.0-preview7.19365.7.

That’s it! You should be ready to go.

Latest Visual Studio preview includes .NET Core 3.0 as the default runtime

The latest preview update for Visual Studio (16.3) includes .NET Core 3.0 as the default .NET Core runtime version. This means that if you install the latest preview of Visual Studio then you already have .NET Core 3.0. New project by default will target .NET Core 3.0

Top level ASP.NET Core templates in Visual Studio

The ASP.NET Core templates now show up as top level templates in Visual Studio in the “Create a new project” dialog.

ASP.NET Core templates

This means you can now search for the various ASP.NET Core templates and filter by project type (web, service, library, etc.) to find the one you want to use.

Simplified web templates

We’ve taken some steps to further simplify the web app templates to reduce the amount of code that is frequently just removed.

Specifically:

  • The cookie consent UI is no longer included in the web app templates by default.
  • Scripts and related static assets are now referenced as local files instead of using CDNs based on the current environment.

We will provide samples and documentation for adding these features to new apps as needed.

Attribute splatting for components

Components can now capture and render additional attributes in addition to the component’s declared parameters. Additional attributes can be captured in a dictionary and then “splatted” onto an element as part of the component’s rendering using the new @attributes Razor directive. This feature is especially valuable when defining a component that produces a markup element that supports a variety of customizations. For instance if you were defining a component that produces an <input> element, it would be tedious to define all of the attributes <input> supports like maxlength or placeholder as component parameters.

Accepting arbitrary parameters

To define a component that accepts arbitrary attributes define a component parameter using the [Parameter] attribute with the CaptureUnmatchedAttributes property set to true. The type of the parameter must be assignable from Dictionary<string, object>. This means that IEnumerable<KeyValuePair<string, object>> or IReadOnlyDictionary<string, object> are also options.

@code { [Parameter(CaptureUnmatchedAttributes = true)] Dictionary<string, object> Attributes { get; set; }
}

The CaptureUnmatchedAttributes property on [Parameter] allows that parameter to match all attributes that do not match any other parameter. A component can only define a single parameter with CaptureUnmatchedAttributes.

Using @attributes to render arbitrary attributes

A component can pass arbitrary attributes to another component or markup element using the @attributes directive attribute. The @attributes directive allows you to specify a collection of attributes to pass to a markup element or component. This is valuable because the set of key-value-pairs specified as attributes can come from a .NET collection and do not need to be specified in the source code of the component.

<input class="form-field" @attributes="Attributes" type="text" /> @code { [Parameter(CaptureUnmatchedAttributes = true)] Dictionary<string, object> Attributes { get; set; }
}

Using the @attributes directive the contents of the Attribute property get “splatted” onto the input element. If this results in duplicate attributes, then evaluation of attributes occurs from left to right. In the above example if Attributes also contained a value for class it would supersede class="form-field". If Attributes contained a value for type then that would be superseded by type="text".

Data binding support for TypeConverters and generics

Blazor now supports data binding to types that have a string TypeConverter. Many built-in framework types, like Guid and TimeSpan have a string TypeConverter, or you can define custom types with a string TypeConverter yourself. These types now work seamlessly with data binding:

<input @bind="guid" /> <p>@guid</p> @code { Guid guid;
}

Data binding also now works great with generics. In generic components you can now bind to types specified using generic type parameters.

@typeparam T <input @bind="value" /> <p>@value</p> @code { T value;
}

Clarified which directive attributes expect HTML vs C

In Preview 6 we introduced directive attributes as a common syntax for Razor compiler related features like specifying event handlers (@onclick) and data binding (@bind). In this update we’ve cleaned up which of the built-in directive attributes expect C# and HTML. Specifically, event handlers now expect C# values so a leading @ character is no longer required when specifying the event handler value:

@* Before *@
<button @onclick="@OnClick">Click me</button> @* After *@
<button @onclick="OnClick">Click me</button>

EventCounters

In place of Windows perf counters, .NET Core introduced a new way of emitting metrics via EventCounters. In preview7, we now emit EventCounters ASP.NET Core. You can use the dotnet counters global tool to view the metrics we emit.

Install the latest preview of dotnet counters by running the following command:

dotnet tool install --global dotnet-counters --version 3.0.0-preview7.19365.2

Hosting

The Hosting EventSourceProvider (Microsoft.AspNetCore.Hosting) now emits the following request counters:

  • requests-per-second
  • total-requests
  • current-requests
  • failed-requests

SignalR

In addition to hosting, SignalR (Microsoft.AspNetCore.Http.Connections) also emits the following connection counters:

  • connections-started
  • connections-stopped
  • connections-timed-out
  • connections-duration

To view all the counters emitted by ASP.NET Core, you can start dotnet counters and specify the desired provider. The example below shows the output when subscribing to events emitted by the Microsoft.AspNetCore.Hosting and System.Runtime providers.

dotnet counters monitor -p <PID> Microsoft.AspNetCore.Hosting System.Runtime

D8GX-5oV4AASKwM

New Package ID for SignalR’s JavaScript Client in NPM

The Azure SignalR Service made it easier for non-.NET developers to make use of SignalR’s real-time capabilities. A frequent question we would get from potential customers who wanted to enable their applications with SignalR via the Azure SignalR Service was “does it only work with ASP.NET?” The former identity of the ASP.NET Core SignalR – which included the @aspnet organization on NPM, only further confused new SignalR users.

To mitigate this confusion, beginning with 3.0.0-preview7, the SignalR JavaScript client will change from being @aspnet/signalr to @microsoft/signalr. To react to this change, you will need to change your references in package.json files, require statements, and ECMAScript import statements. If you’re interested in providing feedback on this move or to learn the thought process the team went through to make the change, read and/or contribute to this GitHub issue where the team engaged in an open discussion with the community.

New Customizable SignalR Hub Method Authorization

With Preview 7, SignalR now provides a custom resource to authorization handlers when a hub method requires authorization. The resource is an instance of HubInvocationContext. The HubInvocationContext includes the HubCallerContext, the name of the hub method being invoked, and the arguments to the hub method.

Consider the example of a chat room allowing multiple organization sign-in via Azure Active Directory. Anyone with a Microsoft account can sign in to chat, but only members of the owning organization should be able to ban users or view users’ chat histories. Furthermore, we might want to restrict certain functionality from certain users. Using the updated features in Preview 7, this is entirely possible. Note how the DomainRestrictedRequirement serves as a custom IAuthorizationRequirement. Now that the HubInvocationContext resource parameter is being passed in, the internal logic can inspect the context in which the Hub is being called and make decisions on allowing the user to execute individual Hub methods.

public class DomainRestrictedRequirement : AuthorizationHandler<DomainRestrictedRequirement, HubInvocationContext>, IAuthorizationRequirement
{ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DomainRestrictedRequirement requirement, HubInvocationContext resource) { if (IsUserAllowedToDoThis(resource.HubMethodName, context.User.Identity.Name) && context.User != null && context.User.Identity != null && context.User.Identity.Name.EndsWith("@jabbr.net", StringComparison.OrdinalIgnoreCase)) { context.Succeed(requirement); } return Task.CompletedTask; } private bool IsUserAllowedToDoThis(string hubMethodName, string currentUsername) { return !(currentUsername.Equals("[email protected]", StringComparison.OrdinalIgnoreCase) && hubMethodName.Equals("banUser", StringComparison.OrdinalIgnoreCase)); }
}

Now, individual Hub methods can be decorated with the name of the policy the code will need to check at run-time. As clients attempt to call individual Hub methods, the DomainRestrictedRequirement handler will run and control access to the methods. Based on the way the DomainRestrictedRequirement controls access, all logged-in users should be able to call the SendMessage method, only users who’ve logged in with a @jabbr.net email address will be able to view users’ histories, and – with the exception of [email protected] – will be able to ban users from the chat room.

[Authorize]
public class ChatHub : Hub
{ public void SendMessage(string message) { } [Authorize("DomainRestricted")] public void BanUser(string username) { } [Authorize("DomainRestricted")] public void ViewUserHistory(string username) { }
}

Creating the DomainRestricted policy is as simple as wiring it up using the authorization middleware. In Startup.cs, add the new policy, providing the custom DomainRestrictedRequirement requirement as a parameter.

services .AddAuthorization(options => { options.AddPolicy("DomainRestricted", policy => { policy.Requirements.Add(new DomainRestrictedRequirement()); }); });

It must be noted that in this example, the DomainRestrictedRequirement class is not only a IAuthorizationRequirement but also it’s own AuthorizationHandler for that requirement. It is fine to split these into separate classes to separate concerns. Yet, in this way, there’s no need to inject the AuthorizationHandler during Startup, since the requirement and the handler are the same thing, there’s no need to inject the handler separately.

HTTPS in gRPC templates

The gRPC templates have been now been updated to use HTTPS by default. At development time, we continue the same certificate generated by the dotnet dev-certs tool and during production, you will still need to supply your own certificate.

gRPC Client Improvements

The managed gRPC client (Grpc.Net.Client) has been updated to target .NET Standard 2.1 and no longer depends on types present only in .NET Core 3.0. This potentially gives us the ability to run on other platforms in the future.

gRPC Metapackage

In 3.0.0-preview7, we’ve introduced a new package Grpc.AspNetCore that transitively references all other runtime and tooling dependencies required for building gRPC projects. Reasoning about a single package version for the metapackage should make it easier for developers to deal with as opposed multiple dependencies that version independently.

CLI tool for managing gRPC code generation

The new dotnet-grpc global tool makes it easier to manage protobuf files and their code generation settings. The global tool manages adding and removing protobuf files as well adding the required package references required to build and run gRPC applications.

Install the latest preview of dotnet-grpc by running the following command:

dotnet tool install --global dotnet-grpc --version 0.1.22-pre2

As an example, you can run following commands to generate a protobuf file and add it to your project for code generation. If you attempt this on a non-web project, we will default to generating a client and add the required package dependencies.

dotnet new proto -o .\Protos\mailbox.proto
dotnet grpc add-file .\Protos\mailbox.proto

Give feedback

We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core and Blazor!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

ASP.NET Core updates in .NET Core 3.0 Preview 4

Daniel Roth

Daniel

.NET Core 3.0 Preview 4 is now available and it includes a bunch of new updates to ASP.NET Core.

Here’s the list of what’s new in this preview:

  • Razor Components renamed back to server-side Blazor
  • Client-side Blazor on WebAssembly now in official preview
  • Resolve components based on @using
  • _Imports.razor
  • New component item template
  • Reconnection to the same server
  • Stateful reconnection after prerendering
  • Render stateful interactive components from Razor pages and views
  • Detect when the app is prerendering
  • Configure the SignalR client for server-side Blazor apps
  • Improved SignalR reconnect features
  • Configure SignalR client for server-side Blazor apps
  • Additional options for MVC service registration
  • Endpoint routing updates
  • New template for gRPC
  • Design-time build for gRPC
  • New Worker SDK

Please see the release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET Core 3.0 Preview 4 install the .NET Core 3.0 Preview 4 SDK

If you’re on Windows using Visual Studio, you also need to install the latest preview of Visual Studio 2019.

If you’re using Visual Studio Code, check out the improved Razor tooling and Blazor support in the C# extension.

Upgrade an existing project

To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 4, follow the migrations steps in the ASP.NET Core docs.

Please also see the full list of breaking changes in ASP.NET Core 3.0.

To upgrade an existing ASP.NET Core 3.0 Preview 3 project to Preview 4:

  • Update Microsoft.AspNetCore.* package references to 3.0.0-preview4-19216-03
  • In Razor Components apps (i.e. server-side Blazor apps) rename _ViewImports.cshtml to _Imports.razor for Razor imports that should apply to Razor components.
  • In Razor Component apps, in your Index.cshtml file, change the tag that references components.server.js so that it references blazor.server.js instead.
  • Remove any use of the _RazorComponentInclude property in your project file and rename and component files using the .cshtml file extension to use the .razor file extension instead.
  • Remove package references to Microsoft.AspNetCore.Components.Server.
  • Replace calls to AddRazorComponents in Startup.ConfigureServices with AddServerSideBlazor.
  • Replace calls to MapComponentHub with MapBlazorHub.
  • Remove any use of the Microsoft.AspNetCore.Components.Services namespace and replace with Microsoft.AspNetCore.Components as required.
  • In Razor Component apps, replace the {*clientPath} route in the host Razor Page with “/” and add a call to MapFallbackToPage in UseEndpoints.
  • Update any call to UseRouting in your Startup.Configure method to move the route mapping logic into a call to UseEndpoints at the point where you want the endpoints to be executed.

Before:

app.UseRouting(routes =>
{ routes.MapRazorPages();
}); app.UseCookiePolicy(); app.UseAuthorization();

After:

app.UseRouting(); app.UseCookiePolicy(); app.UseAuthorization(); app.UseEndpoints(routes =>
{ routes.MapRazorPages(); routes.MapFallbackToPage();
});

Razor Components renamed back to server-side Blazor

For a while, we’ve used the terminology Razor Components in some cases, and Blazor in other cases. This has proven to be confusing, so following a lot of community feedback, we’ve decided to drop the name ASP.NET Core Razor Components, and return to the name Server-side Blazor instead.

This emphasizes that Blazor is a single client app model with multiple hosting models:

  • Server-side Blazor runs on the server via SignalR
  • Client-side Blazor runs client-side on WebAssembly

… but either way, it’s the same programming model. The same Blazor components can be hosted in both environments.

In this preview of the .NET Core SDK we renamed the “Razor Components” template back to “Blazor (server-side)” and updated the related APIs accordingly. In Visual Studio the template will still show up as “Razor Components” when using Visual Studio 2019 16.1.0 Preview 1, but it will start showing up as “Blazor (server-side)” in a subsequent preview. We’ve also updated the template to use the new super cool flaming purple Blazor icon.

Blazor (server-side) template

Client-side Blazor on WebAssembly now in official preview

We’re also thrilled to announce that client-side Blazor on WebAssembly is now in official preview! Blazor is no longer experimental and we are committing to ship it as a supported web UI framework including support for running client-side in the browser on WebAssembly.

  • Server-side Blazor will ship as part of .NET Core 3.0. This was already announced last October.
  • Client-side Blazor won’t ship as part of the initial .NET Core 3.0 release, but we are now announcing it is committed to ship as part of a future .NET Core release (and hence is no longer an “experiment”).

With each preview release of .NET Core 3.0, we will continue to ship preview releases of both server and client-side Blazor.

Resolve components based on @using

Components in referenced assemblies are now always in scope and can be specified using their full type name including the namespace. You no longer need to import components from component libraries using the @addTagHelper directive.

For example, you can add a Counter component to the Index page like this:


Use the @using directive to bring component namespaces into scope just like you would in C# code:

@using BlazorWebApp1.Pages 

_Imports.razor

Use _Imports.razor files to import Razor directives across multiple Razor component files (.razor) in a hierarchical fashion.

For example, the following _Imports.razor file applies a layout and adds using statements for all Razor components in a the same folder and in any sub folders:

@layout MainLayout
@using Microsoft.AspNetCore.Components.
@using BlazorApp1.Data

This is similar to how you can use _ViewImports.cshtml with Razor views and pages, but applied specifically to Razor component files.

New component item template

You can now add components to Blazor apps using the new Razor Component item template:

dotnet new razorcomponent -n MyComponent1

Reconnection to the same server

Server-side Blazor apps require an active SignalR connection to the server to function. In this preview, the app will now attempt to reconnect to the server. As long as the state for that client is still in memory, the client session will resume without losing any state.

When the client detects that the connection has been lost a default UI is displayed to the user while the client attempts to reconnect:

Attempting reconnect

If reconnection failed the user is given the option to retry:

Reconnect failed

To customize this UI define an element with components-reconnect-modal as its ID. The client will update this element with one of the following CSS classes based on the state of the connection:

  • components-reconnect-show: Show the UI to indicate the connection was lost and the client is attempting to reconnect.
  • components-reconnect-hide: The client has an active connection – hide the UI.
  • components-reconnect-failed: Reconnection failed. To attempt reconnection again call window.Blazor.reconnect().

Stateful reconnection after prerendering

Server-side Blazor apps are setup by default to prerender the UI on the server before client connection back to the server is established. This is setup in the _Host.cshtml Razor page:

 @(await Html.RenderComponentAsync()) 
</body>

In this preview the client will now reconnect back to the server to the same state that was used to prerender the app. If the app state is still in memory it doesn’t need to be rerendered once the SignalR connection is established.

Render stateful interactive components from Razor pages and views

You can now add stateful interactive components to a Razor page or View. When the page or view renders the component will be prerendered with it. The app will then reconnect to the component state once the client connection has been established as long as it is still in memory.

For example, the following Razor page renders a Counter component with an initial count that is specified using a form:

<h1>My Razor Page</h1>
<form> <input type="number" asp-for="InitialCount" /> <button type="submit">Set initial count</button>
</form> @(await Html.RenderComponentAsync<Counter>(new { InitialCount = InitialCount })) @functions { [BindProperty(SupportsGet=true)] public int InitialCount { get; set; }
}

Interactive component on Razor page

Detect when the app is prerendering

While a Blazor app is prerendering, certain actions (like calling into JavaScript) are not possible because a connection with the browser has not yet been established. Components may need to render differently when being prerendered.

To delay JavaScript interop calls until after the connection with the browser has been established you can now use the OnAfterRenderAsync component lifecycle event. This event will only be called after the app has been fully rendered and the client connection established.

To conditionally render different content based on whether the app is currently being prerendered or not use IsConnected property on the IComponentContext service. This property will only return true if there is an active connection with the client.

Configure the SignalR client for server-side Blazor apps

Sometimes you need to configure the SignalR client used by server-side Blazor apps. For example, you might want to configure logging on the SignalR client to diagnose a connection issue.

To configure the SignalR client for server-side Blazor apps, add an autostart="false" attribute on the script tag for the blazor.server.js script, and then call Blazor.start passing in a config object that specifies the SignalR builder:

http://_framework/blazor.server.js
 Blazor.start({ configureSignalR: function (builder) { builder.configureLogging(2); // LogLevel.Information } });

Improved SignalR connection lifetime handling

Preview 4 will improve the developer experience for handling SignalR disconnection and reconnection. Automatic reconnects can be enabled by calling the withAutomaticReconnect method on HubConnectionBuilder:

const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect() .build();

Without any parameters, withAutomaticReconnect() will cause the configure the client to try to reconnect, waiting 0, 2, 10 and 30 seconds respectively before between each attempt.

In order to configure a non-default number of reconnect attempts before failure, or to change the reconnect timing, withAutomaticReconnect accepts an array of numbers representing the delay in milliseconds to wait before starting each reconnect attempt.

const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect([0, 0, 2000, 5000]) // defaults to [0, 2000, 10000, 30000] .build();

Improved disconnect & reconnect handling opportunities

Before starting any reconnect attempts, the HubConnection will transition to the Reconnecting state and fire its onreconnecting callback. This provides an opportunity to warn users that the connection has been lost, disable UI elements, and mitigate confusing user scenarios that might occur due to the disconnected state.

connection.onreconnecting((error) => { console.assert(connection.state === signalR.HubConnectionState.Reconnecting); document.getElementById("messageInput").disabled = true; const li = document.createElement("li"); li.textContent = `Connection lost due to error "${error}". Reconnecting.`; document.getElementById("messagesList").appendChild(li);
});

If the client successfully reconnects within its first four attempts, the HubConnection will transition back to the Connected state and fire onreconnected callbacks. This gives developers a good opportunity to inform users the connection has been reestablished.

connection.onreconnected((connectionId) => { console.assert(connection.state === signalR.HubConnectionState.Connected); document.getElementById("messageInput").disabled = false; const li = document.createElement("li"); li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`; document.getElementById("messagesList").appendChild(li);
});

If the client doesn’t successfully reconnect within its first four attempts, the HubConnection will transition to the Disconnected state and fire its onclosed callbacks. This is a good opportunity to inform users the connection has been permanently lost and recommend refreshing the page.

connection.onclose((error) => { console.assert(connection.state === signalR.HubConnectionState.Disconnected); document.getElementById("messageInput").disabled = true; const li = document.createElement("li"); li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`; document.getElementById("messagesList").appendChild(li);
})

Additional options for MVC service registration

We’re adding some new options for registering MVC’s various features inside ConfigureServices.

What’s changing

We’re adding three new top level extension methods related to MVC features on IServiceCollection. Along with this change we are updating our templates to use these new methods instead of AddMvc().

AddMvc() is not being removed and will continue to behave as it does today.

public void ConfigureServices(IServiceCollection services)
{ // Adds support for controllers and API-related features - but not views or pages. // // Used by the API template. services.AddControllers();
}
public void ConfigureServices(IServiceCollection services)
{ // Adds support for controllers, API-related features, and views - but not pages. // // Used by the Web Application (MVC) template. services.AddControllersWithViews();
}
public void ConfigureServices(IServiceCollection services)
{ // Adds support for Razor Pages and minimal controller support. // // Used by the Web Application template. services.AddRazorPages();
}

These new methods can also be combined. This example is equivalent to the current AddMvc().

public void ConfigureServices(IServiceCollection services)
{ services.AddControllers(); services.AddRazorPages();
}

These methods return an IMvcBuilder that can be chained to access any of the methods that are available today from the builder returned by AddMvc().

We recommend using whichever option feels best based on your needs.

Motivations

We wanted to provide some more options that represent how users use the product. In particular we’ve received strong feedback from users that want an API-focused flavor of MVC without the overhead for having the ability to serve views and pages. We tried to provide an experience for this in the past through the AddMvcCore() method, but that approach hasn’t been very successful. Users who tried using AddMvcCore() have been surprised by how much they need to know to use it successfully, and as a result we haven’t promoted its usage. We hope that AddControllers() will better satisfy this scenario.

In addition to the AddControllers() experience, we’re also attempting to create options that feel right for other scenarios. We’ve heard requests for this in the past, but not as strongly as the requests for an API-focused profile. Your feedback about whether AddMvc() could be improved upon, and how will be valuable.

What’s in AddControllers()

AddControllers() includes support for:

  • Controllers
  • Model Binding
  • API Explorer (OpenAPI integration)
  • Authorization [Authorize]
  • CORS [EnableCors]
  • Data Annotations validation [Required]
  • Formatter Mappings (translate a file-extension to a content-type)

All of these features are included because they fit under the API-focused banner, and they are very much pay-for-play. None of these features proactively interact with the request pipeline, these are activated by attributes on your controller or model class. API Explorer is an slight exception, it is a piece of infrastructure used by OpenAPI libraries and will do nothing without Swashbuckle or NSwag.

Some notable features AddMvc() includes but AddControllers() does not:

  • Antiforgery
  • Temp Data
  • Views
  • Pages
  • Tag Helpers
  • Memory Cache

These features are view-related and aren’t necessary in an API-focused profile of MVC.

What’s in AddControllersWithViews()

AddControllersWithViews() includes support for:

  • Controllers
  • Model Binding
  • API Explorer (OpenAPI integration)
  • Authorization [Authorize]
  • CORS [EnableCors]
  • Data Annotations validation [Required]
  • Formatter Mappings (translate a file-extension to a content-type)
  • Antiforgery
  • Temp Data
  • Views
  • Tag Helpers
  • Memory Cache

We wanted to position AddControllersWithViews() as a superset of AddControllers() for simplicity in explaining it. This features set also happens to align with the ASP.NET Core 1.X release (before Razor Pages).

Some notable features AddMvc() includes but AddControllersWithViews() does not:
– Pages

What’s in AddRazorPages()

AddRazorPages() includes support for:

  • Pages
  • Controllers
  • Model Binding
  • Authorization [Authorize]
  • Data Annotations validation [Required]
  • Antiforgery
  • Temp Data
  • Views
  • Tag Helpers
  • Memory Cache

For now this profile includes basic support for controllers, but excludes many of the API-focused features listed below. We’re interested in your feedback about what should be included by default in AddRazorPages().

Some notable features AddMvc() includes but AddRazorPages() does not:

  • API Explorer (OpenAPI integration)
  • CORS [EnableCors]
  • Formatter Mappings (translate a file-extension to a content-type)

Endpoint Routing updates

In ASP.NET Core 2.2 we introduced a new routing implementation called Endpoint Routing which replaces IRouter-based routing for ASP.NET Core MVC. In the upcoming 3.0 release Endpoint Routing will become central to the ASP.NET Core middleware programming model. Endpoint Routing is designed to support greater interoperability between frameworks that need routing (MVC, gRPC, SignalR, and more …) and middleware that want to understand the decisions made by routing (localization, authorization, CORS, and more …).

While it’s still possible to use the old UseMvc() or UseRouter() middleware in a 3.0 application, we recommend that every application migrate to Endpoint Routing if possible. We are taking steps to address compatibility bugs and fill in previously unsupported scenarios. We welcome your feedback about what features are missing or anything else that’s not great about routing in this preview release.

We’ll be uploading another post soon with a conceptual overview and cookbook for Endpoint Routing in 3.0.

Endpoint Routing overview

Endpoint Routing is made up of the pair of middleware created by app.UseRouting() and app.UseEndpoints(). app.UseRouting() marks the position in the middleware pipeline where a routing decision is made – where an endpoint is selected. app.UseEndpoints() marks the position in the middleware pipeline where the selected endpoint is executed. Middleware that run in between these can see the selected endpoint (if any) or can select a different endpoint.

If you’re familiar with routing from using MVC then most of what you have experienced so far will behave the same way. Endpoint Routing understands the same route template syntax and processes URLs in a very similar way to the in-the-box implementations of IRouter. Endpoint routing supports the [Route] and similar attributes inside MVC.

We expect most applications will only require changes to the Startup.cs file.

A typical Configure() method using Endpoint Routing has the following high-level structure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ // Middleware that run before routing. Usually the following appear here: if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Error"); } app.UseStaticFiles() // Runs matching. An endpoint is selected and set on the HttpContext if a match is found. app.UseRouting(); // Middleware that run after routing occurs. Usually the following appear here: app.UseAuthentication() app.UseAuthorization() app.UseCors() // These middleware can take different actions based on the endpoint. // Executes the endpoint that was selected by routing. app.UseEndpoints(endpoints => { // Mapping of endpoints goes here: endpoints.MapControllers() endpoints.MapRazorPages() endpoints.MapHub<MyChatHub>() endpoints.MapGrpcService<MyCalculatorService>() }); // Middleware here will only run if nothing was matched.
}

MVC Controllers, Razor Pages, SignalR, gRPC, and more are added inside UseEndpoints() – they are now part of the same routing system.

New template for gRPC

The gRPC template has been simplified to a single project template. We no longer include a gRPC client as part of the template.
For instructions on how to create a gRPC client, refer to the docs.

.
├── appsettings.Development.json
├── appsettings.json
├── grpc.csproj
├── Program.cs
├── Properties
│   └── launchSettings.json
├── Protos
│   └── greet.proto
├── Services
│   └── GreeterService.cs
└── Startup.cs 3 directories, 8 files

Design-time build for gRPC

Design-time build support for gRPC code-generation makes it easier to rapidly iterate on your gRPC services. Changes to your *.proto files no longer require you to build your project to re-run code generation.

Design time build

Worker SDK

In Preview 3 we introduced the new Worker Service template. In Preview 4 we’ve further decoupled that template from Web by introducing its own SDK. If you create a new Worker Service your csproj will now look like the following:

<Project Sdk="Microsoft.NET.Sdk.Worker"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> <UserSecretsId>dotnet-WebApplication59-A2B1DB8D-0408-4583-80BA-1B32DAE36B97</UserSecretsId> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0-preview4.19216.2" /> </ItemGroup>
</Project>

We’ll have more to share on the new Worker SDK in a future post.

Give feedback

We hope you enjoy the new features in this preview release of ASP.NET Core! Please let us know what you think by filing issues on GitHub.

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

Updated Razor support in Visual Studio Code, now with Blazor support

Daniel Roth

Daniel

Today we are pleased to announce improved Razor tooling support in Visual Studio Code with the latest C# extension. This latest release includes improved Razor diagnostics and support for tag helpers and Blazor apps.

Get Started

To use this preview of Razor support in Visual Studio Code install the following:

To try out Visual Studio Code with Blazor apps, also install:

  • .NET Core 3.0 (Preview 4 or later)
  • The latest Blazor CLI templates:

    dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview4-19216-03
    

What’s new in this release?

Improved diagnostics

We’ve improved the Razor diagnostics in Visual Studio Code for a variety of scenarios, including floating @ characters:

Floating @ character

Missing end braces:

Missing end brace

And missing end tags in code blocks:

Missing end tag

Tag helpers

Tag helper completions are now supported in ASP.NET Core projects:

Tag helper completion

As well as completions for tag helper attribute names and values:

Tag helper attribute completion

Blazor

Visual Studio Code now works with Blazor apps too!

You get completions for components and component parameters:

Component completions

Also data-binding, event handlers and lots of other Blazor goodies!

Blazor todos

Limitations and known issues

This is an alpha release of the Razor tooling for Visual Studio Code, so there are a number of limitations and known issues:

  • Razor editing is currently only supported in ASP.NET Core and Blazor projects (no support for ASP.NET projects)
  • Limited support for colorization

Note that if you need to disable the Razor tooling:

  • Open the Visual Studio Code User Settings: File -> Preferences -> Settings
  • Search for “razor”
  • Check the “Razor: Disabled” checkbox

Feedback

Please let us know what you think about this latest update to the Razor tooling support in Visual Studio Code by reporting issues in the Razor.VSCode repo. When reporting Razor tooling related issues please use the “Report a Razor Issue” command in Visual Studio Code to capture all of the relevant longs and diagnostic information. Just run the command and then follow the instructions.

Thanks for trying out Razor in Visual Studio Code!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

.NET Core Workers in Azure Container Instances

Avatar

In .NET Core 3.0 we are introducing a new type of application template called Worker Service. This template is intended to give you a starting point for writing long running services in .NET Core. In this walkthrough you’ll learn how to use a Worker with Azure Container Registry and Azure Container Instances to get your Worker running as a microservice in the cloud.

Since the Worker template Glenn blogged about is also available via the dotnet new command line, I can create one on my Mac and edit the code using Visual Studio for Mac or Visual Studio Code (which I’ll be using here to take advantage of the integrated Docker extension).

dotnet new worker

I’ll use the default from the Worker template. As it will write to logs during execution via ILogger, I’ll be able to tell quickly from looking in the logs if the Worker is running.

public class Worker : BackgroundService
{ private readonly ILogger<Worker> _logger; public Worker(ILogger<Worker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); } }
}

Visual Studio Code’s Docker tools are intelligent enough to figure out this is a .NET Core app, and will suggest the correct Docker file via the Command Palette’s Add Docker files to workspace option.

By right-clicking the resulting Dockerfile I can build the Worker into a Docker image in one click.

The Build Image option will package my Worker’s code into a Docker container locally. The second option, ACR Tasks: Build Image would use Azure Container Registry Tasks to build the image in the cloud, rather than on disk. This is helpful for scenarios when the base image is larger than I want to download locally or when I’m building an application on a Windows base image from Linux or Mac. You can learn more about ACR Tasks in the ACR docs. The Azure CLI makes it easy to login to the Azure Container Registry using the Azure CLI. This results in my Docker client being authenticated to the Azure Container Registry in my subscription.

az acr login -n BackgroundWorkerImages

This can be done in the VS Code integrated terminal or in the local terminal, as the setting will be persisted across the terminals’ environment. It can’t be done using the cloud shell, since logging into the Azure Container Registry requires local shell access so local Docker images can be accessed. Before I push the container image into my registry, I need to tag the image with the URI of the image once it has been pushed into my registry. I can easily get the ACR instance URI from the portal.

I’ll copy the URI of the registry’s login server in the portal so I can paste it when I tag the image later.

By selecting the backgroundworker:latest image in Visual Studio Code’s Docker explorer pane, I can select Tag Image.

I’ll be prompted for the tag, and I can easily paste in the URI I copied from the portal.

Finally, I can right-click the image tag I created and select Push, and the image will be pushed into the registry. Once I have a Docker image in the registry, I can use the CLI or tools to deploy it to Azure Container Instances, Kubernetes, or even Azure App Service.

Now that the worker is containerized and stored in the registry, starting an instance of it is one click away.

Once the container instance starts up, I’ll see some logs indicating the worker is executing, but these are just the basic startup logs and not my information-level logs I have in my Worker code.

Since I added Information-level logs during the worker’s execution, the configuration in appsettings.json (or the environment variable for the container instance) will need to be updated to see more verbose logs.

{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.Hosting.Lifetime": "Information" } }
}

Once the code is re-packaged into an updated Docker image and pushed into the Azure Container Registry, following a simple Restart…

… more details will be visible in the container instance’s logging output.

The Worker template makes it easy to create long-running background workers that you can run for as long as you need in Azure Container Instances. New container instances can be created using the portal or the Azure Command Line. Or, you can opt for more advanced scenarios using Azure DevOps or Logic Apps. With the Worker template making it easy to get started building microservices using your favorite ASP.NET Core idioms and Azure’s arsenal of container orchestration services you can get your microservices up and running in minutes.

Avatar
Brady Gaster

Senior Program Manager, ASP.NET Core

Follow    

<!–


–>

Posted on Leave a comment

.NET Core Workers as Windows Services

Avatar

Glenn

In .NET Core 3.0 we are introducing a new type of application template called Worker Service. This template is intended to give you a starting point for writing long running services in .NET Core. In this walkthrough we will create a worker and run it as a Windows Service.

Create a worker

Preview Note: In our preview releases the worker template is in the same menu as the Web templates. This will change in a future release. We intend to place the Worker Server template directly inside the create new project wizard.

Create a Worker in Visual Studio

image

image

image

Create a Worker on the command line

Run dotnet new worker

image

Run as a Windows Service

In order to run as a Windows Service we need our worker to listen for start and stop signals from ServiceBase the .NET type that exposes the Windows Service systems to .NET applications. To do this we want to:

Add the Microsoft.Extensions.Hosting.WindowsServices NuGet package

image

Add the UseServiceBaseLifetime call to the HostBuilder in our Program.cs

public class Program
{ public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceBaseLifetime() .ConfigureServices(services => { services.AddHostedService<Worker>(); });
}

This method does a couple of things. First, it checks whether or not the application is actually running as a Windows Service, if it isn’t then it noops which makes this method safe to be called when running locally or when running as a Windows Service. You don’t need to add guard clauses to it and can just run the app normally when not installed as a Windows Service.

Secondly, it configures your host to use a ServiceBaseLifetime. ServiceBaseLifetime works with ServiceBase to help control the lifetime of your app when run as a Windows Service. This overrides the default ConsoleLifetime that handles signals like CTL+C.

Install the Worker

Once we have our worker using the ServiceBaseLifetime we then need to install it:

First, lets publish the application. We will install the Windows Service in-place, meaning the exe will be locked whenever the service is running. The publish step is a nice way to make sure all the files I need to run the service are in one place and ready to be installed.

dotnet publish -o c:\code\workerpub

Then we can use the sc utility in an admin command prompt

sc create workertest binPath=c:\code\workerpub\WorkerTest.exe

For example:

image

Security note: This command has the service run as local system, which isn’t something you will generally want to do. Instead you should create a service account and run the windows service as that account. We will not talk about that here, but there is some documentation on the ASP.NET docs talking about it here: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-2.2

Logging

The logging system has an Event Log provider that can send log message directly to the Windows Event Log. To log to the event log you can add the Microsoft.Extensions.Logging.EventLog package and then modify your Program.cs:

public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureLogging(loggerFactory => loggerFactory.AddEventLog()) .ConfigureServices(services => { services.AddHostedService<Worker>(); });

In upcoming previews we plan to improve the experience of using Workers with Windows Services by:

  1. Rename UseWindowsServiceBaseLifetime to UseWindowsService
  2. Add automatic and improved integration with the Event Log when running as a Windows Service.

We hope you try out this new template and want you to let us know how it goes, you can file any bugs or suggestions here: https://github.com/aspnet/AspNetCore/issues/new/choose

Avatar