04-24-2019, 12:29 AM
ASP.NET Core updates in .NET Core 3.0 Preview 4
<div style="margin: 5px 5% 10px 5%;"><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.jpg" width="150" height="150" title="" alt="" /></div><div><div class="row justify-content-center">
<div class="col-md-4">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.jpg" width="58" height="58" alt="Daniel Roth" class="avatar avatar-58 wp-user-avatar wp-user-avatar-58 alignnone photo"></p>
<p>Daniel</p>
</div>
</div>
</div>
<div class="entry-meta">
<p>April 18th, 2019</p>
</p></div>
<p><!-- .entry-meta --> </p>
<p><a href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-preview-4/">.NET Core 3.0 Preview 4 is now available</a> and it includes a bunch of new updates to ASP.NET Core.</p>
<p>Here’s the list of what’s new in this preview:</p>
<ul>
<li>Razor Components renamed back to server-side Blazor</li>
<li>Client-side Blazor on WebAssembly now in official preview</li>
<li>Resolve components based on <code>@using</code></li>
<li>_Imports.razor</li>
<li>New component item template</li>
<li>Reconnection to the same server</li>
<li>Stateful reconnection after prerendering</li>
<li>Render stateful interactive components from Razor pages and views</li>
<li>Detect when the app is prerendering</li>
<li>Configure the SignalR client for server-side Blazor apps</li>
<li>Improved SignalR reconnect features</li>
<li>Configure SignalR client for server-side Blazor apps</li>
<li>Additional options for MVC service registration</li>
<li>Endpoint routing updates</li>
<li>New template for gRPC</li>
<li>Design-time build for gRPC</li>
<li>New Worker SDK</li>
</ul>
<p>Please see the <a href="https://aka.ms/netcore3releasenotes">release notes</a> for additional details and known issues.</p>
<h2>Get started</h2>
<p>To get started with ASP.NET Core in .NET Core 3.0 Preview 4 <a href="https://aka.ms/netcore3download">install the .NET Core 3.0 Preview 4 SDK</a></p>
<p>If you’re on Windows using Visual Studio, you also need to <a href="https://visualstudio.com/preview">install the latest preview of Visual Studio 2019</a>.</p>
<p>If you’re using <a href="https://code.visualstudio.com">Visual Studio Code</a>, check out the <a href="https://devblogs.microsoft.com/aspnet/updated-razor-support-in-visual-studio-code-now-with-blazor-support/">improved Razor tooling and Blazor support</a> in the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp">C# extension</a>.</p>
<h2>Upgrade an existing project</h2>
<p>To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 4, follow the <a href="https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30">migrations steps in the ASP.NET Core docs</a>.</p>
<p>Please also see the full list of <a href="https://github.com/aspnet/announcements/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A3.0.0+label%3A%22Breaking+change%22">breaking changes</a> in ASP.NET Core 3.0.</p>
<p>To upgrade an existing ASP.NET Core 3.0 Preview 3 project to Preview 4:</p>
<ul>
<li>Update Microsoft.AspNetCore.* package references to 3.0.0-preview4-19216-03</li>
<li>In Razor Components apps (i.e. server-side Blazor apps) rename <em>_ViewImports.cshtml</em> to <em>_Imports.razor</em> for Razor imports that should apply to Razor components.</li>
<li>In Razor Component apps, in your <em>Index.cshtml</em> file, change the <code></code> tag that references <em>components.server.js</em> so that it references <em>blazor.server.js</em> instead.</li>
<li>Remove any use of the <code>_RazorComponentInclude</code> property in your project file and rename and component files using the .cshtml file extension to use the .razor file extension instead.</li>
<li>Remove package references to Microsoft.AspNetCore.Components.Server.</li>
<li>Replace calls to <code>AddRazorComponents</code> in <code>Startup.ConfigureServices</code> with <code>AddServerSideBlazor</code>.</li>
<li>Replace calls to <code>MapComponentHub</code> with <code>MapBlazorHub</code>.</li>
<li>Remove any use of the <code>Microsoft.AspNetCore.Components.Services</code> namespace and replace with <code>Microsoft.AspNetCore.Components</code> as required.</li>
<li>In Razor Component apps, replace the <code>{*clientPath}</code> route in the host Razor Page with “/” and add a call to <code>MapFallbackToPage</code> in <code>UseEndpoints</code>.</li>
<li>Update any call to <code>UseRouting</code> in your <code>Startup.Configure</code> method to move the route mapping logic into a call to <code>UseEndpoints</code> at the point where you want the endpoints to be executed.</li>
</ul>
<p>Before:</p>
<pre><code class="csharp">app.UseRouting(routes =>
{ routes.MapRazorPages();
}); app.UseCookiePolicy(); app.UseAuthorization();
</code></pre>
<p>After:</p>
<pre><code class="csharp">app.UseRouting(); app.UseCookiePolicy(); app.UseAuthorization(); app.UseEndpoints(routes =>
{ routes.MapRazorPages(); routes.MapFallbackToPage();
});
</code></pre>
<h2>Razor Components renamed back to server-side Blazor</h2>
<p>For a while, we’ve used the terminology <em>Razor Components</em> in some cases, and <em>Blazor</em> in other cases. This has proven to be confusing, so following a lot of community feedback, we’ve decided to drop the name <em>ASP.NET Core Razor Components</em>, and return to the name <em>Server-side Blazor</em> instead.</p>
<p>This emphasizes that Blazor is a single client app model with multiple hosting models:</p>
<ul>
<li><strong>Server-side Blazor</strong> runs on the server via SignalR</li>
<li><strong>Client-side Blazor</strong> runs client-side on WebAssembly</li>
</ul>
<p>… but either way, it’s the same programming model. The same Blazor components can be hosted in both environments.</p>
<p>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.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.png" alt="Blazor (server-side) template"></p>
<h2>Client-side Blazor on WebAssembly now in official preview</h2>
<p>We’re also thrilled to <a href="https://devblogs.microsoft.com/aspnet/blazor-now-in-official-preview">announce</a> 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.</p>
<ul>
<li>Server-side Blazor will ship as part of .NET Core 3.0. This was already announced last October.</li>
<li>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”).</li>
</ul>
<p>With each preview release of .NET Core 3.0, we will continue to ship preview releases of both server and client-side Blazor.</p>
<h2>Resolve components based on <code>@using</code></h2>
<p>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 <code>@addTagHelper</code> directive.</p>
<p>For example, you can add a <code>Counter</code> component to the <code>Index</code> page like this:</p>
<pre><code class="html">
</code></pre>
<p>Use the @using directive to bring component namespaces into scope just like you would in C# code:</p>
<pre><code class="html">@using BlazorWebApp1.Pages
</code></pre>
<h2>_Imports.razor</h2>
<p>Use <em>_Imports.razor</em> files to import Razor directives across multiple Razor component files (.razor) in a hierarchical fashion.</p>
<p>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:</p>
<pre><code>@layout MainLayout
@using Microsoft.AspNetCore.Components.
@using BlazorApp1.Data
</code></pre>
<p>This is similar to how you can use <em>_ViewImports.cshtml</em> with Razor views and pages, but applied specifically to Razor component files.</p>
<h2>New component item template</h2>
<p>You can now add components to Blazor apps using the new Razor Component item template:</p>
<pre><code>dotnet new razorcomponent -n MyComponent1
</code></pre>
<h2>Reconnection to the same server</h2>
<p>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.</p>
<p>When the client detects that the connection has been lost a default UI is displayed to the user while the client attempts to reconnect:</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4-1.png" alt="Attempting reconnect"></p>
<p>If reconnection failed the user is given the option to retry:</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4-2.png" alt="Reconnect failed"></p>
<p>To customize this UI define an element with <code>components-reconnect-modal</code> as its ID. The client will update this element with one of the following CSS classes based on the state of the connection:</p>
<ul>
<li><code>components-reconnect-show</code>: Show the UI to indicate the connection was lost and the client is attempting to reconnect.</li>
<li><code>components-reconnect-hide</code>: The client has an active connection – hide the UI.</li>
<li><code>components-reconnect-failed</code>: Reconnection failed. To attempt reconnection again call <code>window.Blazor.reconnect()</code>.</li>
</ul>
<h2>Stateful reconnection after prerendering</h2>
<p>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 <em>_Host.cshtml</em> Razor page:</p>
<pre><code class="html"> @(await Html.RenderComponentAsync())
</body>
</code></pre>
<p>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.</p>
<h2>Render stateful interactive components from Razor pages and views</h2>
<p>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.</p>
<p>For example, the following Razor page renders a <code>Counter</code> component with an initial count that is specified using a form:</p>
<pre><code class="html"><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; }
}
</code></pre>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.gif" alt="Interactive component on Razor page"></p>
<h2>Detect when the app is prerendering</h2>
<p>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.</p>
<p>To delay JavaScript interop calls until after the connection with the browser has been established you can now use the <code>OnAfterRenderAsync</code> component lifecycle event. This event will only be called after the app has been fully rendered and the client connection established.</p>
<p>To conditionally render different content based on whether the app is currently being prerendered or not use <code>IsConnected</code> property on the <code>IComponentContext</code> service. This property will only return true if there is an active connection with the client.</p>
<h2>Configure the SignalR client for server-side Blazor apps</h2>
<p>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.</p>
<p>To configure the SignalR client for server-side Blazor apps, add an <code>autostart="false"</code> attribute on the script tag for the <em>blazor.server.js</em> script, and then call <code>Blazor.start</code> passing in a config object that specifies the SignalR builder:</p>
<pre><code class="html"><a href="http://_framework/blazor.server.js">http://_framework/blazor.server.js</a>
Blazor.start({ configureSignalR: function (builder) { builder.configureLogging(2); // LogLevel.Information } });
</code></pre>
<h2>Improved SignalR connection lifetime handling</h2>
<p>Preview 4 will improve the developer experience for handling SignalR disconnection and reconnection. Automatic reconnects can be enabled by calling the <code>withAutomaticReconnect</code> method on <a href="http://docs.microsoft.com/javascript/api/%40aspnet/signalr/hubconnectionbuilder">HubConnectionBuilder</a>:</p>
<pre><code class="javascript">const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect() .build();
</code></pre>
<p>Without any parameters, <code>withAutomaticReconnect()</code> will cause the configure the client to try to reconnect, waiting 0, 2, 10 and 30 seconds respectively before between each attempt.</p>
<p>In order to configure a non-default number of reconnect attempts before failure, or to change the reconnect timing, <code>withAutomaticReconnect</code> accepts an array of numbers representing the delay in milliseconds to wait before starting each reconnect attempt.</p>
<pre><code class="javascript">const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect([0, 0, 2000, 5000]) // defaults to [0, 2000, 10000, 30000] .build();
</code></pre>
<h3>Improved disconnect & reconnect handling opportunities</h3>
<p>Before starting any reconnect attempts, the <code>HubConnection</code> will transition to the <code>Reconnecting</code> state and fire its <code>onreconnecting</code> 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.</p>
<pre><code class="javascript">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);
});
</code></pre>
<p>If the client successfully reconnects within its first four attempts, the <code>HubConnection</code> will transition back to the <code>Connected</code> state and fire <code>onreconnected</code> callbacks. This gives developers a good opportunity to inform users the connection has been reestablished.</p>
<pre><code class="javascript">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);
});
</code></pre>
<p>If the client doesn’t successfully reconnect within its first four attempts, the <code>HubConnection</code> will transition to the <code>Disconnected</code> state and fire its <code>onclosed</code> callbacks. This is a good opportunity to inform users the connection has been permanently lost and recommend refreshing the page.</p>
<pre><code class="javascript">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);
})
</code></pre>
<h2>Additional options for MVC service registration</h2>
<p>We’re adding some new options for registering MVC’s various features inside <code>ConfigureServices</code>.</p>
<h3>What’s changing</h3>
<p>We’re adding three new top level extension methods related to MVC features on <code>IServiceCollection</code>. Along with this change we are updating our templates to use these new methods instead of <code>AddMvc()</code>.</p>
<p><em><code>AddMvc()</code> is not being removed and will continue to behave as it does today.</em></p>
<pre><code class="csharp">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();
}
</code></pre>
<pre><code class="csharp">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();
}
</code></pre>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ // Adds support for Razor Pages and minimal controller support. // // Used by the Web Application template. services.AddRazorPages();
}
</code></pre>
<p>These new methods can also be combined. This example is equivalent to the current <code>AddMvc()</code>.</p>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ services.AddControllers(); services.AddRazorPages();
}
</code></pre>
<p>These methods return an <code>IMvcBuilder</code> that can be chained to access any of the methods that are available today from the builder returned by <code>AddMvc()</code>.</p>
<p>We recommend using whichever option feels best based on your needs.</p>
<h3>Motivations</h3>
<p>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 <em>API-focused</em> flavor of MVC without the overhead for having the <em>ability</em> to serve views and pages. We tried to provide an experience for this in the past through the <code>AddMvcCore()</code> method, but that approach hasn’t been very successful. Users who tried using <code>AddMvcCore()</code> 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 <code>AddControllers()</code> will better satisfy this scenario.</p>
<p>In addition to the <code>AddControllers()</code> experience, we’re also attempting to create options that <em>feel right</em> 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 <code>AddMvc()</code> could be improved upon, and how will be valuable.</p>
<h3>What’s in AddControllers()</h3>
<p><code>AddControllers()</code> includes support for:</p>
<ul>
<li>Controllers</li>
<li>Model Binding</li>
<li>API Explorer (OpenAPI integration)</li>
<li>Authorization <code>[Authorize]</code> </li>
<li>CORS <code>[EnableCors]</code></li>
<li>Data Annotations validation <code>[Required]</code></li>
<li>Formatter Mappings (translate a file-extension to a content-type)</li>
</ul>
<p>All of these features are included because they fit under the <em>API-focused</em> 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.</p>
<p>Some notable features <code>AddMvc()</code> includes but <code>AddControllers()</code> does not:</p>
<ul>
<li>Antiforgery</li>
<li>Temp Data</li>
<li>Views</li>
<li>Pages</li>
<li>Tag Helpers</li>
<li>Memory Cache</li>
</ul>
<p>These features are view-related and aren’t necessary in an API-focused profile of MVC.</p>
<h3>What’s in AddControllersWithViews()</h3>
<p><code>AddControllersWithViews()</code> includes support for:</p>
<ul>
<li>Controllers</li>
<li>Model Binding</li>
<li>API Explorer (OpenAPI integration)</li>
<li>Authorization <code>[Authorize]</code> </li>
<li>CORS <code>[EnableCors]</code></li>
<li>Data Annotations validation <code>[Required]</code></li>
<li>Formatter Mappings (translate a file-extension to a content-type)</li>
<li>Antiforgery</li>
<li>Temp Data</li>
<li>Views</li>
<li>Tag Helpers</li>
<li>Memory Cache</li>
</ul>
<p>We wanted to position <code>AddControllersWithViews()</code> as a superset of <code>AddControllers()</code> for simplicity in explaining it. This features set also happens to align with the ASP.NET Core 1.X release (before Razor Pages).</p>
<p>Some notable features <code>AddMvc()</code> includes but <code>AddControllersWithViews()</code> does not:<br />– Pages</p>
<h3>What’s in AddRazorPages()</h3>
<p><code>AddRazorPages()</code> includes support for:</p>
<ul>
<li>Pages</li>
<li>Controllers</li>
<li>Model Binding</li>
<li>Authorization <code>[Authorize]</code> </li>
<li>Data Annotations validation <code>[Required]</code></li>
<li>Antiforgery</li>
<li>Temp Data</li>
<li>Views</li>
<li>Tag Helpers</li>
<li>Memory Cache</li>
</ul>
<p>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 <code>AddRazorPages()</code>.</p>
<p>Some notable features <code>AddMvc()</code> includes but <code>AddRazorPages()</code> does not:</p>
<ul>
<li>API Explorer (OpenAPI integration)</li>
<li>CORS <code>[EnableCors]</code></li>
<li>Formatter Mappings (translate a file-extension to a content-type)</li>
</ul>
<h2>Endpoint Routing updates</h2>
<p>In ASP.NET Core 2.2 we introduced a new routing implementation called Endpoint Routing which replaces <code>IRouter</code>-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 …).</p>
<p>While it’s still possible to use the old <code>UseMvc()</code> or <code>UseRouter()</code> 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.</p>
<p>We’ll be uploading another post soon with a conceptual overview and cookbook for Endpoint Routing in 3.0.</p>
<h3>Endpoint Routing overview</h3>
<p>Endpoint Routing is made up of the pair of middleware created by <code>app.UseRouting()</code> and <code>app.UseEndpoints()</code>. <code>app.UseRouting()</code> marks the position in the middleware pipeline where a routing decision is made – where an endpoint is selected. <code>app.UseEndpoints()</code> 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.</p>
<p>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 <code>IRouter</code>. Endpoint routing supports the <code>[Route]</code> and similar attributes inside MVC.</p>
<p>We expect most applications will only require changes to the <code>Startup.cs</code> file.</p>
<p>A typical <code>Configure()</code> method using Endpoint Routing has the following high-level structure:</p>
<pre><code class="csharp">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.
}
</code></pre>
<p>MVC Controllers, Razor Pages, SignalR, gRPC, and more are added inside <code>UseEndpoints()</code> – they are now part of the same routing system.</p>
<h2>New template for gRPC</h2>
<p>The gRPC template has been simplified to a single project template. We no longer include a gRPC client as part of the template.<br />For instructions on how to create a gRPC client, refer to the <a href="https://go.microsoft.com/fwlink/?linkid=2086909">docs</a>.</p>
<pre><code>.
├── appsettings.Development.json
├── appsettings.json
├── grpc.csproj
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Protos
│ └── greet.proto
├── Services
│ └── GreeterService.cs
└── Startup.cs 3 directories, 8 files
</code></pre>
<h2>Design-time build for gRPC</h2>
<p>Design-time build support for gRPC code-generation makes it easier to rapidly iterate on your gRPC services. Changes to your <code>*.proto</code> files no longer require you to build your project to re-run code generation.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4-1.gif" alt="Design time build"></p>
<h2>Worker SDK</h2>
<p>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:</p>
<pre><code class="xml"><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>
</code></pre>
<p>We’ll have more to share on the new Worker SDK in a future post.</p>
<h2>Give feedback</h2>
<p>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 <a href="https://github.com/aspnet/aspnetcore/issues">GitHub</a>.</p>
<div class="authorinfoarea">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.jpg" width="96" height="96" alt="Daniel Roth" class="avatar avatar-96 wp-user-avatar wp-user-avatar-96 alignnone photo"></div>
<div>
<h5><a class="no-underline" aria-label="Daniel Roth" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/" rel="noopener noreferrer">Daniel Roth</a></h5>
<p>Principal Program Manager, ASP.NET</p>
<p><strong>Follow Daniel</strong> <a class="no-underline stayinformed" aria-label="Daniel Roth Twitter profile" target="_blank" href="https://twitter.com/danroth27" rel="noopener noreferrer"><i class="fa fa-twitter"></i></a><a class="no-underline stayinformed" aria-label="Daniel Roth GitHub profile" target="_blank" href="https://github.com/danroth27" rel="noopener noreferrer"><i class="fa fa-github"></i></a><a class="no-underline stayinformed hvr-pop" aria-label="Daniel Roth RSS Feed" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/feed/" rel="noopener noreferrer"></a></p>
</p></div>
</p></div>
</div>
<div style="margin: 5px 5% 10px 5%;"><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.jpg" width="150" height="150" title="" alt="" /></div><div><div class="row justify-content-center">
<div class="col-md-4">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.jpg" width="58" height="58" alt="Daniel Roth" class="avatar avatar-58 wp-user-avatar wp-user-avatar-58 alignnone photo"></p>
<p>Daniel</p>
</div>
</div>
</div>
<div class="entry-meta">
<p>April 18th, 2019</p>
</p></div>
<p><!-- .entry-meta --> </p>
<p><a href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-preview-4/">.NET Core 3.0 Preview 4 is now available</a> and it includes a bunch of new updates to ASP.NET Core.</p>
<p>Here’s the list of what’s new in this preview:</p>
<ul>
<li>Razor Components renamed back to server-side Blazor</li>
<li>Client-side Blazor on WebAssembly now in official preview</li>
<li>Resolve components based on <code>@using</code></li>
<li>_Imports.razor</li>
<li>New component item template</li>
<li>Reconnection to the same server</li>
<li>Stateful reconnection after prerendering</li>
<li>Render stateful interactive components from Razor pages and views</li>
<li>Detect when the app is prerendering</li>
<li>Configure the SignalR client for server-side Blazor apps</li>
<li>Improved SignalR reconnect features</li>
<li>Configure SignalR client for server-side Blazor apps</li>
<li>Additional options for MVC service registration</li>
<li>Endpoint routing updates</li>
<li>New template for gRPC</li>
<li>Design-time build for gRPC</li>
<li>New Worker SDK</li>
</ul>
<p>Please see the <a href="https://aka.ms/netcore3releasenotes">release notes</a> for additional details and known issues.</p>
<h2>Get started</h2>
<p>To get started with ASP.NET Core in .NET Core 3.0 Preview 4 <a href="https://aka.ms/netcore3download">install the .NET Core 3.0 Preview 4 SDK</a></p>
<p>If you’re on Windows using Visual Studio, you also need to <a href="https://visualstudio.com/preview">install the latest preview of Visual Studio 2019</a>.</p>
<p>If you’re using <a href="https://code.visualstudio.com">Visual Studio Code</a>, check out the <a href="https://devblogs.microsoft.com/aspnet/updated-razor-support-in-visual-studio-code-now-with-blazor-support/">improved Razor tooling and Blazor support</a> in the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp">C# extension</a>.</p>
<h2>Upgrade an existing project</h2>
<p>To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 4, follow the <a href="https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30">migrations steps in the ASP.NET Core docs</a>.</p>
<p>Please also see the full list of <a href="https://github.com/aspnet/announcements/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A3.0.0+label%3A%22Breaking+change%22">breaking changes</a> in ASP.NET Core 3.0.</p>
<p>To upgrade an existing ASP.NET Core 3.0 Preview 3 project to Preview 4:</p>
<ul>
<li>Update Microsoft.AspNetCore.* package references to 3.0.0-preview4-19216-03</li>
<li>In Razor Components apps (i.e. server-side Blazor apps) rename <em>_ViewImports.cshtml</em> to <em>_Imports.razor</em> for Razor imports that should apply to Razor components.</li>
<li>In Razor Component apps, in your <em>Index.cshtml</em> file, change the <code></code> tag that references <em>components.server.js</em> so that it references <em>blazor.server.js</em> instead.</li>
<li>Remove any use of the <code>_RazorComponentInclude</code> property in your project file and rename and component files using the .cshtml file extension to use the .razor file extension instead.</li>
<li>Remove package references to Microsoft.AspNetCore.Components.Server.</li>
<li>Replace calls to <code>AddRazorComponents</code> in <code>Startup.ConfigureServices</code> with <code>AddServerSideBlazor</code>.</li>
<li>Replace calls to <code>MapComponentHub</code> with <code>MapBlazorHub</code>.</li>
<li>Remove any use of the <code>Microsoft.AspNetCore.Components.Services</code> namespace and replace with <code>Microsoft.AspNetCore.Components</code> as required.</li>
<li>In Razor Component apps, replace the <code>{*clientPath}</code> route in the host Razor Page with “/” and add a call to <code>MapFallbackToPage</code> in <code>UseEndpoints</code>.</li>
<li>Update any call to <code>UseRouting</code> in your <code>Startup.Configure</code> method to move the route mapping logic into a call to <code>UseEndpoints</code> at the point where you want the endpoints to be executed.</li>
</ul>
<p>Before:</p>
<pre><code class="csharp">app.UseRouting(routes =>
{ routes.MapRazorPages();
}); app.UseCookiePolicy(); app.UseAuthorization();
</code></pre>
<p>After:</p>
<pre><code class="csharp">app.UseRouting(); app.UseCookiePolicy(); app.UseAuthorization(); app.UseEndpoints(routes =>
{ routes.MapRazorPages(); routes.MapFallbackToPage();
});
</code></pre>
<h2>Razor Components renamed back to server-side Blazor</h2>
<p>For a while, we’ve used the terminology <em>Razor Components</em> in some cases, and <em>Blazor</em> in other cases. This has proven to be confusing, so following a lot of community feedback, we’ve decided to drop the name <em>ASP.NET Core Razor Components</em>, and return to the name <em>Server-side Blazor</em> instead.</p>
<p>This emphasizes that Blazor is a single client app model with multiple hosting models:</p>
<ul>
<li><strong>Server-side Blazor</strong> runs on the server via SignalR</li>
<li><strong>Client-side Blazor</strong> runs client-side on WebAssembly</li>
</ul>
<p>… but either way, it’s the same programming model. The same Blazor components can be hosted in both environments.</p>
<p>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.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.png" alt="Blazor (server-side) template"></p>
<h2>Client-side Blazor on WebAssembly now in official preview</h2>
<p>We’re also thrilled to <a href="https://devblogs.microsoft.com/aspnet/blazor-now-in-official-preview">announce</a> 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.</p>
<ul>
<li>Server-side Blazor will ship as part of .NET Core 3.0. This was already announced last October.</li>
<li>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”).</li>
</ul>
<p>With each preview release of .NET Core 3.0, we will continue to ship preview releases of both server and client-side Blazor.</p>
<h2>Resolve components based on <code>@using</code></h2>
<p>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 <code>@addTagHelper</code> directive.</p>
<p>For example, you can add a <code>Counter</code> component to the <code>Index</code> page like this:</p>
<pre><code class="html">
</code></pre>
<p>Use the @using directive to bring component namespaces into scope just like you would in C# code:</p>
<pre><code class="html">@using BlazorWebApp1.Pages
</code></pre>
<h2>_Imports.razor</h2>
<p>Use <em>_Imports.razor</em> files to import Razor directives across multiple Razor component files (.razor) in a hierarchical fashion.</p>
<p>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:</p>
<pre><code>@layout MainLayout
@using Microsoft.AspNetCore.Components.
@using BlazorApp1.Data
</code></pre>
<p>This is similar to how you can use <em>_ViewImports.cshtml</em> with Razor views and pages, but applied specifically to Razor component files.</p>
<h2>New component item template</h2>
<p>You can now add components to Blazor apps using the new Razor Component item template:</p>
<pre><code>dotnet new razorcomponent -n MyComponent1
</code></pre>
<h2>Reconnection to the same server</h2>
<p>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.</p>
<p>When the client detects that the connection has been lost a default UI is displayed to the user while the client attempts to reconnect:</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4-1.png" alt="Attempting reconnect"></p>
<p>If reconnection failed the user is given the option to retry:</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4-2.png" alt="Reconnect failed"></p>
<p>To customize this UI define an element with <code>components-reconnect-modal</code> as its ID. The client will update this element with one of the following CSS classes based on the state of the connection:</p>
<ul>
<li><code>components-reconnect-show</code>: Show the UI to indicate the connection was lost and the client is attempting to reconnect.</li>
<li><code>components-reconnect-hide</code>: The client has an active connection – hide the UI.</li>
<li><code>components-reconnect-failed</code>: Reconnection failed. To attempt reconnection again call <code>window.Blazor.reconnect()</code>.</li>
</ul>
<h2>Stateful reconnection after prerendering</h2>
<p>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 <em>_Host.cshtml</em> Razor page:</p>
<pre><code class="html"> @(await Html.RenderComponentAsync())
</body>
</code></pre>
<p>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.</p>
<h2>Render stateful interactive components from Razor pages and views</h2>
<p>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.</p>
<p>For example, the following Razor page renders a <code>Counter</code> component with an initial count that is specified using a form:</p>
<pre><code class="html"><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; }
}
</code></pre>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.gif" alt="Interactive component on Razor page"></p>
<h2>Detect when the app is prerendering</h2>
<p>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.</p>
<p>To delay JavaScript interop calls until after the connection with the browser has been established you can now use the <code>OnAfterRenderAsync</code> component lifecycle event. This event will only be called after the app has been fully rendered and the client connection established.</p>
<p>To conditionally render different content based on whether the app is currently being prerendered or not use <code>IsConnected</code> property on the <code>IComponentContext</code> service. This property will only return true if there is an active connection with the client.</p>
<h2>Configure the SignalR client for server-side Blazor apps</h2>
<p>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.</p>
<p>To configure the SignalR client for server-side Blazor apps, add an <code>autostart="false"</code> attribute on the script tag for the <em>blazor.server.js</em> script, and then call <code>Blazor.start</code> passing in a config object that specifies the SignalR builder:</p>
<pre><code class="html"><a href="http://_framework/blazor.server.js">http://_framework/blazor.server.js</a>
Blazor.start({ configureSignalR: function (builder) { builder.configureLogging(2); // LogLevel.Information } });
</code></pre>
<h2>Improved SignalR connection lifetime handling</h2>
<p>Preview 4 will improve the developer experience for handling SignalR disconnection and reconnection. Automatic reconnects can be enabled by calling the <code>withAutomaticReconnect</code> method on <a href="http://docs.microsoft.com/javascript/api/%40aspnet/signalr/hubconnectionbuilder">HubConnectionBuilder</a>:</p>
<pre><code class="javascript">const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect() .build();
</code></pre>
<p>Without any parameters, <code>withAutomaticReconnect()</code> will cause the configure the client to try to reconnect, waiting 0, 2, 10 and 30 seconds respectively before between each attempt.</p>
<p>In order to configure a non-default number of reconnect attempts before failure, or to change the reconnect timing, <code>withAutomaticReconnect</code> accepts an array of numbers representing the delay in milliseconds to wait before starting each reconnect attempt.</p>
<pre><code class="javascript">const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect([0, 0, 2000, 5000]) // defaults to [0, 2000, 10000, 30000] .build();
</code></pre>
<h3>Improved disconnect & reconnect handling opportunities</h3>
<p>Before starting any reconnect attempts, the <code>HubConnection</code> will transition to the <code>Reconnecting</code> state and fire its <code>onreconnecting</code> 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.</p>
<pre><code class="javascript">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);
});
</code></pre>
<p>If the client successfully reconnects within its first four attempts, the <code>HubConnection</code> will transition back to the <code>Connected</code> state and fire <code>onreconnected</code> callbacks. This gives developers a good opportunity to inform users the connection has been reestablished.</p>
<pre><code class="javascript">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);
});
</code></pre>
<p>If the client doesn’t successfully reconnect within its first four attempts, the <code>HubConnection</code> will transition to the <code>Disconnected</code> state and fire its <code>onclosed</code> callbacks. This is a good opportunity to inform users the connection has been permanently lost and recommend refreshing the page.</p>
<pre><code class="javascript">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);
})
</code></pre>
<h2>Additional options for MVC service registration</h2>
<p>We’re adding some new options for registering MVC’s various features inside <code>ConfigureServices</code>.</p>
<h3>What’s changing</h3>
<p>We’re adding three new top level extension methods related to MVC features on <code>IServiceCollection</code>. Along with this change we are updating our templates to use these new methods instead of <code>AddMvc()</code>.</p>
<p><em><code>AddMvc()</code> is not being removed and will continue to behave as it does today.</em></p>
<pre><code class="csharp">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();
}
</code></pre>
<pre><code class="csharp">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();
}
</code></pre>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ // Adds support for Razor Pages and minimal controller support. // // Used by the Web Application template. services.AddRazorPages();
}
</code></pre>
<p>These new methods can also be combined. This example is equivalent to the current <code>AddMvc()</code>.</p>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ services.AddControllers(); services.AddRazorPages();
}
</code></pre>
<p>These methods return an <code>IMvcBuilder</code> that can be chained to access any of the methods that are available today from the builder returned by <code>AddMvc()</code>.</p>
<p>We recommend using whichever option feels best based on your needs.</p>
<h3>Motivations</h3>
<p>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 <em>API-focused</em> flavor of MVC without the overhead for having the <em>ability</em> to serve views and pages. We tried to provide an experience for this in the past through the <code>AddMvcCore()</code> method, but that approach hasn’t been very successful. Users who tried using <code>AddMvcCore()</code> 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 <code>AddControllers()</code> will better satisfy this scenario.</p>
<p>In addition to the <code>AddControllers()</code> experience, we’re also attempting to create options that <em>feel right</em> 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 <code>AddMvc()</code> could be improved upon, and how will be valuable.</p>
<h3>What’s in AddControllers()</h3>
<p><code>AddControllers()</code> includes support for:</p>
<ul>
<li>Controllers</li>
<li>Model Binding</li>
<li>API Explorer (OpenAPI integration)</li>
<li>Authorization <code>[Authorize]</code> </li>
<li>CORS <code>[EnableCors]</code></li>
<li>Data Annotations validation <code>[Required]</code></li>
<li>Formatter Mappings (translate a file-extension to a content-type)</li>
</ul>
<p>All of these features are included because they fit under the <em>API-focused</em> 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.</p>
<p>Some notable features <code>AddMvc()</code> includes but <code>AddControllers()</code> does not:</p>
<ul>
<li>Antiforgery</li>
<li>Temp Data</li>
<li>Views</li>
<li>Pages</li>
<li>Tag Helpers</li>
<li>Memory Cache</li>
</ul>
<p>These features are view-related and aren’t necessary in an API-focused profile of MVC.</p>
<h3>What’s in AddControllersWithViews()</h3>
<p><code>AddControllersWithViews()</code> includes support for:</p>
<ul>
<li>Controllers</li>
<li>Model Binding</li>
<li>API Explorer (OpenAPI integration)</li>
<li>Authorization <code>[Authorize]</code> </li>
<li>CORS <code>[EnableCors]</code></li>
<li>Data Annotations validation <code>[Required]</code></li>
<li>Formatter Mappings (translate a file-extension to a content-type)</li>
<li>Antiforgery</li>
<li>Temp Data</li>
<li>Views</li>
<li>Tag Helpers</li>
<li>Memory Cache</li>
</ul>
<p>We wanted to position <code>AddControllersWithViews()</code> as a superset of <code>AddControllers()</code> for simplicity in explaining it. This features set also happens to align with the ASP.NET Core 1.X release (before Razor Pages).</p>
<p>Some notable features <code>AddMvc()</code> includes but <code>AddControllersWithViews()</code> does not:<br />– Pages</p>
<h3>What’s in AddRazorPages()</h3>
<p><code>AddRazorPages()</code> includes support for:</p>
<ul>
<li>Pages</li>
<li>Controllers</li>
<li>Model Binding</li>
<li>Authorization <code>[Authorize]</code> </li>
<li>Data Annotations validation <code>[Required]</code></li>
<li>Antiforgery</li>
<li>Temp Data</li>
<li>Views</li>
<li>Tag Helpers</li>
<li>Memory Cache</li>
</ul>
<p>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 <code>AddRazorPages()</code>.</p>
<p>Some notable features <code>AddMvc()</code> includes but <code>AddRazorPages()</code> does not:</p>
<ul>
<li>API Explorer (OpenAPI integration)</li>
<li>CORS <code>[EnableCors]</code></li>
<li>Formatter Mappings (translate a file-extension to a content-type)</li>
</ul>
<h2>Endpoint Routing updates</h2>
<p>In ASP.NET Core 2.2 we introduced a new routing implementation called Endpoint Routing which replaces <code>IRouter</code>-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 …).</p>
<p>While it’s still possible to use the old <code>UseMvc()</code> or <code>UseRouter()</code> 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.</p>
<p>We’ll be uploading another post soon with a conceptual overview and cookbook for Endpoint Routing in 3.0.</p>
<h3>Endpoint Routing overview</h3>
<p>Endpoint Routing is made up of the pair of middleware created by <code>app.UseRouting()</code> and <code>app.UseEndpoints()</code>. <code>app.UseRouting()</code> marks the position in the middleware pipeline where a routing decision is made – where an endpoint is selected. <code>app.UseEndpoints()</code> 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.</p>
<p>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 <code>IRouter</code>. Endpoint routing supports the <code>[Route]</code> and similar attributes inside MVC.</p>
<p>We expect most applications will only require changes to the <code>Startup.cs</code> file.</p>
<p>A typical <code>Configure()</code> method using Endpoint Routing has the following high-level structure:</p>
<pre><code class="csharp">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.
}
</code></pre>
<p>MVC Controllers, Razor Pages, SignalR, gRPC, and more are added inside <code>UseEndpoints()</code> – they are now part of the same routing system.</p>
<h2>New template for gRPC</h2>
<p>The gRPC template has been simplified to a single project template. We no longer include a gRPC client as part of the template.<br />For instructions on how to create a gRPC client, refer to the <a href="https://go.microsoft.com/fwlink/?linkid=2086909">docs</a>.</p>
<pre><code>.
├── appsettings.Development.json
├── appsettings.json
├── grpc.csproj
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Protos
│ └── greet.proto
├── Services
│ └── GreeterService.cs
└── Startup.cs 3 directories, 8 files
</code></pre>
<h2>Design-time build for gRPC</h2>
<p>Design-time build support for gRPC code-generation makes it easier to rapidly iterate on your gRPC services. Changes to your <code>*.proto</code> files no longer require you to build your project to re-run code generation.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4-1.gif" alt="Design time build"></p>
<h2>Worker SDK</h2>
<p>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:</p>
<pre><code class="xml"><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>
</code></pre>
<p>We’ll have more to share on the new Worker SDK in a future post.</p>
<h2>Give feedback</h2>
<p>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 <a href="https://github.com/aspnet/aspnetcore/issues">GitHub</a>.</p>
<div class="authorinfoarea">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/04/asp-net-core-updates-in-net-core-3-0-preview-4.jpg" width="96" height="96" alt="Daniel Roth" class="avatar avatar-96 wp-user-avatar wp-user-avatar-96 alignnone photo"></div>
<div>
<h5><a class="no-underline" aria-label="Daniel Roth" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/" rel="noopener noreferrer">Daniel Roth</a></h5>
<p>Principal Program Manager, ASP.NET</p>
<p><strong>Follow Daniel</strong> <a class="no-underline stayinformed" aria-label="Daniel Roth Twitter profile" target="_blank" href="https://twitter.com/danroth27" rel="noopener noreferrer"><i class="fa fa-twitter"></i></a><a class="no-underline stayinformed" aria-label="Daniel Roth GitHub profile" target="_blank" href="https://github.com/danroth27" rel="noopener noreferrer"><i class="fa fa-github"></i></a><a class="no-underline stayinformed hvr-pop" aria-label="Daniel Roth RSS Feed" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/feed/" rel="noopener noreferrer"></a></p>
</p></div>
</p></div>
</div>