✨ Shield now has support for Avalonia UI

.NET 8 Preview 7 Features

Oct 11, 2023 | .NET

Alright, devs, who’s up for some next-level tech talk? Preview 7 for .NET 8 has landed, and this is pretty much the final preview we’re looking at before the grand release. We’re going to deep-dive into what this newest kid on the block has in store for us, mainly focusing on System.Text.Json and codegen – our key game-changers. If .NET 8 hasn’t caught your eye yet, the time is now!

.NET Conf 2023: Mark Your Calendars!

Pop the champagne and toss the confetti! We’ve got the dates for .NET Conf 2023. It’s happening from November 14-16, 2023. Can’t you feel the electricity in the air already?

A release coupled with a conference? Now, that’s a double whammy of excitement! Be there to see .NET 8 in its full glory – showcased, celebrated, and thoroughly dissected.

System.Text.Json: New and Improved

Get your party hats on, people! System.Text.Json in .NET 8 Preview 7 is jam-packed with enhancements. Brace yourselves as we’re about to take a plunge into a sea of new features and improvements – all in one go!

JsonSourceGenerationOptionsAttribute – Feature Parity at Its Best

What’s the latest scoop? We now have the JsonSourceGenerationOptionsAttribute matching feature for feature with the distinguished JsonSerializerOptions class. You wonder if it’s noteworthy?

Let me assure you, it is!

This development implies we now have the ability to define serialization settings during the compilation phase. That’s quite a significant benefit, wouldn’t you agree?

Check this out:

    AllowExtraCommas = true,
    ConversionAdapters = new[] { typeof(JsonEnumeratedStringConverter<MyEnumeratedType>) },
    BufferDefaultSize =  1024,
    ConditionToIgnoreByDefault = JsonIgnoreCondition.WhenWritingDefault,
    KeyPolicyForDictionary = JsonKnownNamingPolicy.SnakeUpperCase,
    ExcludeReadOnlyFields = true,
    ExcludeReadOnlyProperties = true,
    IncludeClassFields = true,
    MaximumDepth = 1024,
    HandlingOfNumbers = JsonNumberHandling.Stringify,
    PreferredMethodOfObjectCreation = JsonObjectCreationHandling.Overwrite,
    IgnoreCaseInPropertyNames = true,
    NamingPolicyForProperties = JsonKnownNamingPolicy.KebabUpperCase,
    CommentReadHandling = JsonCommentHandling.Bypass,
    HandlingOfUnknownTypes = JsonUnknownTypeHandling.JsonNodeElement,
    HandlingOfUnmappedMembers = JsonUnmappedMemberHandling.Reject,
    IndentWhileWriting = true)]
public partial class ContextClass : JsonSerializerContext { }

What’s happening here? Well, this example generates a MyContext.Default property, preconfiguration included. This means that all the related options are neatly set in place. And the cherry on top? It takes source generation to a completely new level.

Built-in Support for Memory<T>/ReadOnlyMemory<T>

Kudos to Microsoft for being so forward-thinking! The serializer now inherently supports Memory<T> and ReadOnlyMemory<T> types. The repercussions? Well, let me articulate this clearly: arrays are granted a whole different layer of semantics!

Check out these examples:

// The next line will serialize to a JSON array.
JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); // It returns [1,2,3]

// The subsequent line produces a Base64-encoded string.
JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 }); // The output is "AQID"

Say Hello to Half, Int128 and UInt128

Strap in for some groundbreaking news – Half, Int128, and UInt128 types now enjoy built-in serialization support. And yes, it is a big deal!

Let’s see it running:

// Demonstrating serialization functionality
Console.WriteLine(JsonSerializer.Serialize(new object[] { Half.MaxValue, Int128.MaxValue, UInt128.MaxValue }));
// The result is [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]

Welcoming JsonIncludeAttribute and JsonConstructorAttribute

Prepare for enhanced versatility! The JsonInclude and JsonConstructor attribute annotations now enable the inclusion of non-public members into the serialization contract. It’s certainly a notable advancement!

Here’s an example:

string serializedJson = JsonSerializer.Serialize(new PocoClass(42)); // Gives {"X":42}
JsonSerializer.Deserialize<PocoClass>(serializedJson); // Deserializes the string

public class PocoClass
    internal PocoClass(int value) => X = value;

    internal int X { get; private set; }

Meet IJsonTypeInfoResolver.WithAddedModifier

Here’s a fun new feature. The extension method IJsonTypeInfoResolver.WithAddedModifier lets you make modifications to the serialization contracts of any IJsonTypeInfoResolver instance. What can I say, Microsoft sure knows how to keep the party going.

var setupOptions = new JsonSerializerOptions
    TypeInfoResolver = CustomContext.Default
        .WithAdditionModifier(static typeInfo =>
            foreach (JsonPropertyInfo property in typeInfo.Properties)
                property.Name = property.Name.ToUpperInvariant();

JsonSerializer.Serialize(new PocoRecord(42), setupOptions); // Returns {"VALUE":42}

public record PocoRecord(int value);

public partial class CustomContext : JsonSerializerContext { }

Say ‘Hi’ to the Additional JsonNode Functionality

Let’s hear it for JsonNode, which is ramped up with much-improved functionality.

For instance, it now supports deep cloning:

JsonNode node = JsonNode.Parse("{\"Prop\":{\"NestedProp\":42}}");
JsonNode other = node.DeepClone();
bool same = JsonNode.DeepEquals(node, other); // The output returns 'true'.

It also supports IEnumerable now:

// Just to show how support for `IEnumerable` works:
JsonArray jsonArray = new JsonArray(1, 2, 3, 2);
IEnumerable<int> values = jsonArray.GetValues<int>().Where(i => i == 2);

Next-level Hosting with Microsoft.Extensions.Hosting.IHostedLifecycleService

Behold the fantastic upgrade in hosted services! With the addition of IHostedLifeCycleService, it extends your control over the application lifecycle. While IHostedService already had StartAsync and StopAsync, this new interface adds a suite of methods – StartingAsync, StartedAsync, StoppingAsync, StoppedAsync. In simple words, now you can handle events not just at start and stop, but also right before start and right after completion. How cool is that?

How to Use IHostedLifecycleService

Now, let’s dive deep and understand how to employ this newfound power. Take a quick peek at the code snippet below:

// Begin by establishing the host builder.
IHostBuilder mainHostBuilder = new HostBuilder();
mainHostBuilder.ConfigureServices(serviceCollection =>
    // Incorporate your service.

// Proceed to build and activate the host.
using (IHost mainHost = mainHostBuilder.Build())
    await mainHost.StartAsync();

// Ensure your service adopts the new interface.
public class CustomService : IHostedLifeCycleService
    // Subsequently, execute the furnished lifecycle hooks to accommodate your requirements.
    public Task OnStartingAsync(CancellationToken cancelToken) => /* incorporate logic here */ Task.CompletedTask;
    public Task OnStartAsync(CancellationToken cancelToken) => /* incorporate logic here */ Task.CompletedTask;
    public Task OnStartedAsync(CancellationToken cancelToken) => /* incorporate logic here */ Task.CompletedTask;
    public Task OnStopAsync(CancellationToken cancelToken) => /* incorporate logic here */ Task.CompletedTask;
    public Task OnStoppedAsync(CancellationToken cancelToken) => /* incorporate logic here */ Task.CompletedTask;
    public Task OnStoppingAsync(CancellationToken cancelToken) => /* incorporate logic here */ Task.CompletedTask;

So, there you go. With this new interface, you can now have methods running just before and after your application starts and stops. Awesome, right?

Keyed Services Support in Microsoft.Extensions.DependencyInjection

Esteemed audience, witness the integration of Keyed services support in the reliable Microsoft.Extensions.DependencyInjection. Curious about its advantages? This intelligent feature from Microsoft enables enhanced segregation of service registration and usage. Quite the timely innovation, wouldn’t you agree?

Rollin’ with Keyed Services

Now, are you all excited to know how to use it? Thought so! Let’s dig right into it.

// Begin by using the essential services from Microsoft.
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

// Configure the application 
var applicationBuilder = WebApplication.CreateBuilder(args);

// Add your data consumers

// Introduce your main performers! Use AddKeyedSingleton to add services.
applicationBuilder.Services.AddKeyedSingleton<IMemoryCache, LargeCache>("large");
applicationBuilder.Services.AddKeyedSingleton<IMemoryCache, CompactCache>("compact");

// After completion, build your application.
var application = applicationBuilder.Build();

// Configure routing.
application.MapGet("/large", (LargeCacheUser data) => data.RetrieveData());
application.MapGet("/compact", (CompactCacheUser data) => data.RetrieveData());

// Lastly, run your application.

// Use the [FromKeyedServices] attribute to specify the key associated with your services.
class LargeCacheUser([FromKeyedServices("large")] IMemoryCache cache)
    public object? RetrieveData() => cache.Get("dataset");

class CompactCacheUser(IKeyedServiceProvider keyedServiceProvider)
    // Get services associated with a specified key.
    public object? RetrieveData() => keyedServiceProvider.GetRequiredKeyedService<IMemoryCache>("compact");

Ta-da! And that’s how you ace the game with Keyed Services. Enjoy scoping your services with keys and make your code cleaner and neater. Are you ready to go key-searching in your code? Now go ahead and make keys your best friends! Cheers to easy and efficient code! Let’s get our codes key-ready, shall we?

You May Also Like