static void

Net 8 Highlights

Published Wednesday 15 November 2023

Having covered the Net8 links, here's some highlights.

It's not a huge game-changing release. We can just change the TargetFramework and get some performance benefits, without rewriting or adding anything. The biggest changes are probably in Blazor, which seem to fulfil the promise of the technology. Only two new C# constructs, both of which are a bit more concise: collection initializers and primary constructors. The AOT is potentially interesting, but probably something for later .net versions. First Microsoft have to rework more of ASP, and then common libraries and techniques need to evolve to be compatible.

Performance

There are lots of perf improvements under the covers, in .net, asp, blazor...

Blazor

Blazor had 2 main models that look similar but are fundamentally different; server-side razor pages/websockets and compiled to browser webassembly. Net8 is a major release to add a new mode, Server-side rendering (SSR), and provide a simple way to move between the models (rendermode) within the same application, and even the same page (what was global to the project can now be component based).

NB: the _Imports.razor should have a import to refer to rendermode

@using static Microsoft.AspNetCore.Components.Web.RenderMode

Components/pages can have the rendermode directive - default (not specified) is static (or technically, inherit from parent), the other options are InteractiveServer, InteractiveClient and InteractiveAuto (= server, then client). You can specify this on the top of routable pages, or specify it on the component instance. InteractiveClient components must live in the client project, not the server project (so you download them; you don't want to download server-side components with entity framework etc).

@rendermode InteractiveAuto

This is the server side configuration for interactive server/client components.

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);

    // Add services
    builder.Services.AddRazorComponents()
        //add interactive SERVER components (websockets)
        .AddInteractiveServerComponents()
        //add interactive WASM components (in the browser)
        .AddInteractiveWebAssemblyComponents();

    var app = builder.Build();

    // Other stuff omitted.
    app.UseStaticFiles();

    app.MapRazorComponents<App>()
        .AddInteractiveServerRenderMode()
        .AddInteractiveWebAssemblyRenderMode()
        .AddAdditionalAssemblies(typeof(ClientSideCmponent).Assembly);

    app.Run();
}

There's a new extension, .webcil, for webassembly, to avoid downloading .dll files which corporate security used to block.

Asp.net - keyed services and routing

You could do keyed services with 3rd party DI frameworks like Autofac, but now the built-in DI supports it (add an attribute [FromKeyedServices(key)]).

builder.Services.AddKeyedScoped<IMyService, MyService>("Key");

Consuming in Blazor with an attribute:

[Inject(Key = "key")]
public IMyService Service { getset; } = default!;

Visual Studio colour-codes routes and uses analysers to spot problems.

Aspire (preview)

Aspire is a builder for cloud (i.e. Azure) applications. It's intended for developers, and uses C# code (not yaml!) to orchestrate your dependencies into containers. Most of the configuration glue is done for you (including specifying ports and connection strings, using internal service discovery), and it automatically adds best practices like metrics, logging and the Polly retry project. There are two new Visual Studio projects, AppHost (the startup project) and ServiceDefaults. Launching the AppHost brings up a developer dashboard to check the status.

Primary ctors on classes

You could do it with (immutable) Records before, but now you can do them on classes. You don't need constructor boilerplate- it is defined on the class line (you can also have conventional ctor overloads). Note the parameters are not hidden fields, they are initialization values; the values are captured in the properties. For simpler classes and DI, this makes sense. If you need logic, you need explicit constructors.

public class Person(string name)
{
    public string Name { get; } = name;
}

Collection initializing

Until .net 8 you initialize arrays/lists with curly brackets {} and a type or new().

var array = new[] { 1, 2, 3 };
var list = new List<int> { 1, 2, 3 };

Now you can use square brackets [] instead of curlies (like in javascript). It's always an enumerable to fill any array/list type.

You can concatenate/shallow copy stuff with the .. spread operator (also familiar from javascript)

int[] part1 = [1, 2, 3];
int[] part2 = [4, 5, 6];
int[] all = [..part1, ..part2, 7, 8, 9];

AOT

AOT/Ahead-of-time compilation compiles .net into native code, so it starts faster and runs quicker.

The huge limitation is you can't use reflection, which is used all over the place (any serialization including json, regex, things like automapper or even your DI scanning an assembly to map the interfaces to classes). Source generators (compile-time rewriting) can remove the reflection, and the asp team has done it for MinimalApis, but not for MVC/webapi/blazor.

Previously: Net8 (Tuesday 14 November 2023)