Friday, May 23, 2025

New Features in .Net 10



🚀 Runtime Enhancements

Stack Allocation for Small Arrays

The Just-In-Time (JIT) compiler now optimizes memory usage by stack-allocating small arrays of reference types when their scope is limited, reducing heap allocations and improving performance. Microsoft Learn

Advanced Code Layout Optimization

A new approach to method code organization uses a 3-opt heuristic to improve hot path density and reduce branch distances, enhancing runtime performance. Microsoft Learn

AVX10.2 Support

.NET 10 introduces support for AVX10.2 instructions on x64 processors, enabling advanced vector operations for high-performance computing tasks. Microsoft Learn

Enhanced Escape Analysis

The JIT compiler's escape analysis now includes local struct fields, allowing more objects to be stack-allocated, reducing garbage collection overhead. Microsoft Learn+1Microsoft for Developers+1


💻 SDK and CLI Improvements

Native Container Image Creation

Console applications can now natively create container images, simplifying deployment in containerized environments. Microsoft Learn

Enhanced CLI Experience

The .NET CLI now generates native tab-completion scripts for popular shells, improving developer productivity. Microsoft Learn


🌐 ASP.NET Core and Blazor Updates

Blazor Enhancements

  • QuickGrid RowClass Parameter: Allows dynamic styling of grid rows based on data conditions.

  • Static Web Assets: Blazor scripts are now served as static web assets, reducing load times by up to 76%.

  • Route Syntax Highlighting: Improves route template visualization in editors. Chandradev's Blog+1DEVCLASS+1

OpenAPI 3.1 Support

ASP.NET Core now supports OpenAPI 3.1, enabling more accurate and expressive API documentation. DEVCLASS


🧠 Language Features

C# 14 Innovations

  • Field-Backed Properties: Access compiler-generated backing fields using the field keyword.

  • Enhanced nameof Expression: Supports unbound generic types.

  • Span<T> Implicit Conversions: Improves performance-critical code.

  • Lambda Parameter Modifiers: Allows ref, in, or out modifiers in lambda expressions.

  • Partial Constructors and Events: Extends partial method capabilities.

  • Static Extension Methods: Introduces extension blocks for static and instance members. Microsoft Learn+1Microsoft+1

F# Enhancements

F# in .NET 10 includes new language features, standard library improvements, and compiler service updates, enhancing functional programming capabilities. Bacancy+3Microsoft Learn+3Medium+3


📱 .NET MAUI Advancements

.NET Multi-platform App UI (MAUI) receives several updates:

  • Improved Tooling: Enhances developer experience across platforms.

  • Performance Boosts: Optimizes rendering and responsiveness.

  • Expanded Platform Support: Adds support for emerging platforms like AR/VR devices. Microsoft Learn+3Medium+3Microsoft+3


🗄️ Entity Framework Core 10

Entity Framework Core 10 introduces:

  • LINQ Enhancements: Provides more expressive queries.

  • Performance Optimizations: Improves data access speed.

  • Azure Cosmos DB Support: Enhances compatibility with cloud-based databases.


📦 Library Improvements

  • Async Zip APIs: Introduces asynchronous methods for ZIP file manipulation.

  • GZipStream Performance: Improves handling of concatenated streams.

  • Out-of-Proc Trace Support: Adds tracing capabilities for activity events and links. Microsoft for Developers


🔮 Looking Ahead

.NET 10 is slated for general availability in November 2025, with LTS support extending through November 2028. Developers can explore the preview releases to prepare for the transition and leverage the new features in their applications





Tuesday, May 13, 2025

What is the Outbox design pattern?

 


Microservices architectures are designed to be scalable, resilient, and independent, but they introduce significant challenges in ensuring data consistency across distributed systems. One critical issue is reliably delivering messages or events between services, especially when network failures or system crashes occur. The Outbox Pattern is a proven design pattern that addresses this challenge by ensuring reliable message delivery in distributed systems. This blog post explores the Outbox Pattern, its importance in microservices, and how to implement it in .NET microservices, complete with practical examples and best practices.

What is the Outbox Pattern?

The Outbox Pattern is a design pattern used in distributed systems, particularly microservices, to ensure reliable message delivery and maintain data consistency. It addresses the problem of message loss that can occur when a service updates its database but fails to send a message to another service or external system due to a crash or network issue.

Why is the Outbox Pattern Needed?

In a microservices architecture, each service typically has its own database, and services communicate asynchronously using message brokers like RabbitMQ, Kafka, or Azure Service Bus. A common challenge arises when a service performs a database transaction and then attempts to send a message. If the service crashes after committing the transaction but before sending the message, the message is lost, leading to inconsistencies across services.

The Outbox Pattern solves this by ensuring that the storage of the message and the business transaction are part of the same atomic operation. This guarantees that messages are not lost, even in the event of a failure.

Core Concept

  • Storing Messages: Instead of directly sending a message to an external system, the service stores the message in a dedicated "outbox" table within its database as part of the same transaction as the business operation.

  • Processing Messages: A separate background process periodically reads messages from the outbox and sends them to their intended destinations, such as another microservice or a message broker.

  • Reliability: If the system crashes, the messages remain in the outbox and can be processed later, ensuring at-least-once delivery.


How Does the Outbox Pattern Work?

The Outbox Pattern operates in the following steps:

  1. Storing Messages in the Outbox:

    • During a business transaction (e.g., creating an order), the service saves the transaction data and simultaneously stores a message in the outbox table.

    • The message is typically serialized (e.g., as JSON) and includes metadata such as the event type and payload.

  2. Processing the Outbox:

    • A background process, such as a scheduled job using Quartz.NET, periodically checks the outbox for unprocessed messages.

    • For each message, it attempts to send it to the intended destination, such as a message broker or another service.

    • Upon successful delivery, the message is marked as processed in the outbox table.

  3. Handling Failures:

    • If sending a message fails (e.g., due to network issues), the message remains in the outbox with a "pending" status.

    • The background process retries sending the message, ensuring at-least-once delivery.

Implementation in .NET Microservices

Implementing the Outbox Pattern in .NET microservices involves defining the outbox table, saving messages during transactions, and processing those messages asynchronously. Below is a step-by-step guide with code examples.

Step 1: Define the Outbox Table

Create a database table to store outbox messages. For example, using SQL Server:

CREATE TABLE OutboxMessages (

    Id INT PRIMARY KEY IDENTITY,

    OccurredOn DATETIME NOT NULL,

    Type NVARCHAR(255) NOT NULL,

    Data NVARCHAR(MAX) NOT NULL,

    ProcessedDate DATETIME NULL

);

Table Fields:

Field

Description

Id

Unique identifier for the message.

OccurredOn

Timestamp when the message was created.

Type

Type of the event (e.g., "UserRegistered").

Data

Serialized payload of the message (e.g., JSON).

ProcessedDate

Timestamp when the message was processed (null if pending).


Step 2: Save Messages to the Outbox

When performing a business operation, save the corresponding message to the outbox within the same transaction. Below is an example in C# using Entity Framework Core:

public class UserService

{

    private readonly AppDbContext _dbContext;


    public UserService(AppDbContext dbContext)

    {

        _dbContext = dbContext;

    }


    public async Task RegisterUserAsync(User user)

    {

        using (var transaction = await _dbContext.Database.BeginTransactionAsync())

        {

            // Save the user to the database

            await _dbContext.Users.AddAsync(user);

            await _dbContext.SaveChangesAsync();


            // Create and save the outbox message

            var notification = new UserRegisteredNotification(user.Id);

            var outboxMessage = new OutboxMessage

            {

                Type = notification.GetType().Name,

                Data = JsonConvert.SerializeObject(notification),

                OccurredOn = DateTime.UtcNow

            };

            await _dbContext.OutboxMessages.AddAsync(outboxMessage);

            await _dbContext.SaveChangesAsync();


            // Commit the transaction

            await transaction.CommitAsync();

        }

    }

}


public class User

{

    public int Id { get; set; }

    public string Name { get; set; }

}


public class UserRegisteredNotification

{

    public int UserId { get; }


    public UserRegisteredNotification(int userId)

    {

        UserId = userId;

    }

}


public class OutboxMessage

{

    public int Id { get; set; }

    public DateTime OccurredOn { get; set; }

    public string Type { get; set; }

    public string Data { get; set; }

    public DateTime? ProcessedDate { get; set; }

}

Key Points:

  • The user and the notification are saved in the same transaction to ensure atomicity.

  • JsonConvert.SerializeObject from Newtonsoft.Json is used to serialize the notification object.

Step 3: Process the Outbox

Use a background service to periodically process the outbox messages. Below is an example using Quartz.NET for scheduling:

[DisallowConcurrentExecution]

public class ProcessOutboxJob : IJob

{

    private readonly IServiceProvider _serviceProvider;

    private readonly IMediator _mediator;


    public ProcessOutboxJob(IServiceProvider serviceProvider, IMediator mediator)

    {

        _serviceProvider = serviceProvider;

        _mediator = mediator;

    }


    public async Task Execute(IJobExecutionContext context)

    {

        using (var scope = _serviceProvider.CreateScope())

        {

            var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();

            var unprocessedMessages = await dbContext.OutboxMessages

                .Where(m => m.ProcessedDate == null)

                .ToListAsync();


            foreach (var message in unprocessedMessages)

            {

                try

                {

                    // Deserialize the message

                    var notificationType = Type.GetType(message.Type);

                    var notification = JsonConvert.DeserializeObject(message.Data, notificationType);


                    // Publish the notification (e.g., to a message broker or via Mediator)

                    await _mediator.Publish(notification);


                    // Mark the message as processed

                    message.ProcessedDate = DateTime.UtcNow;

                    await dbContext.SaveChangesAsync();

                }

                catch (Exception ex)

                {

                    // Log the exception and retry later

                    Console.WriteLine($"Failed to process message {message.Id}: {ex.Message}");

                }

            }

        }

    }

}

Key Points:

  • The job fetches unprocessed messages (ProcessedDate is null).

  • Messages are deserialized and published using a mediator pattern (e.g., MediatR) or sent to a message broker.

  • The [DisallowConcurrentExecution] attribute ensures that only one instance of the job runs at a time.

  • Exceptions are logged, allowing the message to remain in the outbox for retry.

Step 4: Configure Quartz.NET

Configure Quartz.NET to run the ProcessOutboxJob periodically (e.g., every 15 seconds):

public class Startup

{

    public void ConfigureServices(IServiceCollection services)

    {

        services.AddQuartz(q =>

        {

            q.UseMicrosoftDependencyInjectionJobFactory();

            var jobKey = new JobKey("ProcessOutboxJob");

            q.AddJob<ProcessOutboxJob>(opts => opts.WithIdentity(jobKey));

            q.AddTrigger(opts => opts

                .ForJob(jobKey)

                .WithIdentity("ProcessOutboxTrigger")

                .WithCronSchedule("0/15 * * ? * *")); // Run every 15 seconds

        });

        services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);

    }

}

Best Practices and Considerations

To ensure a robust implementation of the Outbox Pattern, consider the following best practices:

  • Idempotency: Receiving services must be idempotent to handle potential duplicate messages, as the Outbox Pattern guarantees at-least-once delivery. For example, use unique message IDs and check for duplicates before processing.

  • Monitoring: Monitor the outbox table for messages that remain unprocessed for too long, which may indicate issues with the background process or external systems.

  • Retry Logic: Implement retry mechanisms in the background process to handle transient failures, such as network issues or temporary unavailability of message brokers.

  • Cleanup: Periodically archive or delete processed messages from the outbox to prevent table bloat and maintain performance.

  • Error Handling: Log exceptions during message processing and consider implementing a dead-letter queue for messages that repeatedly fail to process.

Real-World Example

Consider an e-commerce application where a customer places an order. The ordering service needs to notify the inventory service to update stock and the notification service to send a confirmation email. Using the Outbox Pattern:

  1. The ordering service saves the order to its database and adds an "OrderCreated" event to the outbox table within the same transaction.

  2. A background process, running via Quartz.NET, picks up the "OrderCreated" event and sends it to a message broker (e.g., RabbitMQ).

  3. The inventory and notification services subscribe to the message broker, receive the event, and perform their respective tasks.

  4. If the ordering service crashes after saving the order but before sending the event, the event remains in the outbox and is processed later, ensuring no data is lost.

This approach ensures data consistency across services, even in the face of failures.

Advantages and Disadvantages

Advantages

  • Consistency: Ensures that messages are not lost during transactions, maintaining data consistency across services.

  • Reliability: Provides at-least-once delivery guarantees, making the system more robust.

  • Decoupling: Allows services to operate independently, communicating asynchronously via the outbox and message brokers.

Disadvantages

  • Complexity: Adds additional components, such as the outbox table and background processor, increasing system complexity.

  • Performance Overhead: Introduces extra database operations for storing and processing messages, which may impact performance.

  • Idempotency Requirement: Receiving services must be designed to handle duplicate messages, which can be challenging to implement correctly.

Comparison with Other Patterns

The Outbox Pattern is often compared to other microservices patterns, such as the Saga Pattern:

  • Outbox Pattern: Focuses on reliable message delivery by storing messages in a database before sending them. It is typically used for individual service-level messaging.

  • Saga Pattern: Manages distributed transactions across multiple services, often using the Outbox Pattern as a component to ensure reliable event publishing. For example, a saga orchestrator might use the Outbox Pattern to send commands to participating services.

The Outbox Pattern can be combined with the Saga Pattern for complex workflows, as demonstrated in a proof-of-concept implementation using Debezium and Kafka (Saga Orchestration).

Conclusion

The Outbox Pattern is a powerful tool for ensuring reliable message delivery in .NET microservices. By storing messages in a database and processing them asynchronously, it helps maintain data consistency and resilience in distributed systems. While it introduces some complexity, the benefits in terms of reliability and decoupling make it a valuable pattern for building robust microservices architectures. By following the implementation steps and best practices outlined in this post, .NET developers can create systems that handle distributed communication effectively.

For further exploration, check out the sample implementation on GitHub (Sample CQRS API) and consider integrating the Outbox Pattern with message brokers and other microservices patterns for even more robust solutions.

Friday, May 9, 2025

What is GraphQL and How Can It Be Used?

 


Getting Started with GraphQL in .NET 8

A practical, end‑to‑end primer using Hot Chocolate & Strawberry Shake   


Why GraphQL?

If you’ve built—or consumed—REST APIs you’ve felt the pain of under‑ and over‑fetching:

* Needing three endpoints to hydrate one screen
* Getting twenty properties when you only need two

GraphQL fixes that with a single, flexible endpoint where the client asks for exactly what it needs and nothing more. It also supports real‑time subscriptions (think dashboards, chat, IoT feeds) without bolting on a separate signal‑plane.


The .NET ecosystem

  • Hot Chocolate – the premier GraphQL server for ASP.NET Core, now on v14 ChiliCream

  • Strawberry Shake – a type‑safe, code‑gen GraphQL client for any .NET platform ChiliCream

They’re both from the same maintainers (ChilliCream) and play together perfectly.


1 Server Setup with Hot Chocolate

Target: .NET 8 Minimal‑API web app

  1. Add packages


dotnet add package HotChocolate.AspNetCore dotnet add package HotChocolate.Subscriptions
  1. Program.cs


var builder = WebApplication.CreateBuilder(args); builder.Services .AddGraphQLServer() .AddQueryType<Query>() // read‑only operations .AddMutationType<Mutation>() // writes .AddSubscriptionType<Subscription>() // real‑time events .AddInMemorySubscriptions(); // dev‑friendly message broker var app = builder.Build(); app.MapGraphQL(); // maps /graphql + WebSockets app.Run();

What those calls do

CallPurpose (one‑liner)
AddGraphQLServer()Registers the GraphQL middleware, execution engine, schema services, and Banana Cake Pop IDE.
AddQueryType<T>() / AddMutationType<T>() / AddSubscriptionType<T>()Maps your C# classes to the root Query, Mutation, and Subscription types in the schema.
AddInMemorySubscriptions()Adds an in‑memory pub/sub broker so your mutations can raise events the subscriptions will receive. Ideal for development; swap for Redis, Kafka, etc. in production.
  1. Domain & resolver classes


public record Book(int Id, string Title, string Author); public record Movie(int Id, string Title, string Director); public class Query { // Simple demo query public IEnumerable<Book> GetBooks() => _books; private static readonly List<Book> _books = [ new(1,"Clean Code","Robert C. Martin") ]; } public class Mutation { public async Task<Book> AddBook( string title, string author, [Service] ITopicEventSender sender) { var book = new Book(Random.Shared.Next(1000, 9999), title, author); await sender.SendAsync(nameof(Subscription.BookAdded), book); return book; } // AddMovie omitted for brevity – same pattern } public class Subscription { [Subscribe, Topic] public Book BookAdded([EventMessage] Book b) => b; }

Run the project and browse to /graphql. Banana Cake Pop appears with schema introspection and an interactive playground.


mutation { addBook(title:"Dune", author:"Frank Herbert") { id title author } }

Open a second tab:


subscription { bookAdded { id title author } }

Add a book in tab 1 → the event streams into tab 2. ✨


2 Consuming the API from a .NET Client

Option A – Quick & Dirty HttpClient

Great for scripts/tests:


var http = new HttpClient { BaseAddress = new("https://localhost:5001/graphql") }; var gql = new { query = @"query { books { id title author } }" }; var res = await http.PostAsJsonAsync("", gql); var json = await res.Content.ReadAsStringAsync(); Console.WriteLine(json);

(No strong typing, no IDE help.)

Option B – Strawberry Shake (type‑safe)

  1. Install tooling (once per machine)


dotnet tool install -g StrawberryShake.Tools
  1. Create client project


dotnet new console -n BooksClient cd BooksClient dotnet add package StrawberryShake.Transport.WebSockets
  1. Init & generate


dotnet graphql init http://localhost:5001/graphql dotnet graphql generate

The tool:

  • Downloads the schema

  • Generates C# models & operations

  • Adds IBooksClient to DI

  1. Use it


await using var host = Host .CreateDefaultBuilder() .ConfigureServices(s => s .AddBooksClient() // extension generated for you .ConfigureHttpClient(c => c.BaseAddress = new("https://localhost:5001/graphql"))) .Build(); var client = host.Services.GetRequiredService<IBooksClient>(); // mutation var add = await client.AddBook.ExecuteAsync("Foundation", "Isaac Asimov"); Console.WriteLine($"Created: {add.Data!.AddBook.Id}"); // subscription await foreach (var ev in client.BookAdded.Watch()) { Console.WriteLine($"New book: {ev.Data!.BookAdded.Title}"); }

You now have compile‑time safety, auto‑completed queries, and integrated WebSocket subscriptions.


3 Where to Go Next

  • Pagination & filtering – Hot Chocolate supports Relay‑style cursors, projections, and filtering middleware.

  • Authorization – Decorate resolver methods with [Authorize] or integrate ASP.NET Core policies.

  • Production subscriptions – Swap AddInMemorySubscriptions() for Redis or Kafka back‑planes.

  • Monitoring – Hot Chocolate exposes events for OpenTelemetry tracing and metrics ChiliCream.


## Bonus: Subscriptions in Depth — Real‑Time GraphQL for your .NET apps
(drop this straight into the post after the “Where to Go Next” section)


### Why you care 🔔

  • Live dashboards & tickers – push deltas instead of polling REST.

  • Chat, collaboration, IoT – duplex or server‑push communication over one endpoint.

  • Cleaner backend code – one mutation can publish an event that any number of clients subscribe to.


### 1 Transports: WebSocket vs SSE

TransportDirectionSpecs Hot Chocolate SupportsNotes
WebSocketsfull‑duplexgraphql‑ws (recommended) and legacy subscriptions‑transport‑ws ChiliCreamWorks everywhere, but needs 80/443 → WS upgrade.
SSE (GraphQL‑SSE)server→clientapplication/stream+json via the GraphQL‑SSE protocol (v13+) ChiliCreamFirewalls love it; great for dashboards where clients don’t need to talk back.

In ASP.NET Core you get WebSockets by default (app.UseWebSockets()), and Hot Chocolate wires up SSE automatically if the client requests it.


### 2 Choosing a Pub/Sub Provider

ProviderWhen to useNuGet & DI
In‑Memory (default)Local dev / single nodeHotChocolate.Subscriptions.AddInMemorySubscriptions()
RedisMulti‑node, cheap & battle‑testedHotChocolate.Subscriptions.Redis.AddRedisSubscriptions(o ⇒ ConnectionMultiplexer.Connect("redis:6379")) ChiliCream
Kafka / Azure SB / RabbitMassive fan‑out or enterprise infraCommunity packages or write ITopicEventSender / ITopicEventReceiver adapters
csharp
builder.Services .AddGraphQLServer() .AddQueryType<Query>() .AddMutationType<Mutation>() .AddSubscriptionType<Subscription>() .AddRedisSubscriptions(o => ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("Redis")));

### 3 Securing Subscriptions

  1. Handshake auth – add a JWT or bearer token in the initial connection_init payload or WebSocket/SSE headers.

  2. Per‑field rules – decorate resolver methods with [Authorize] just like queries/mutations.

  3. Lifecycle hooks – implement ISocketSessionInterceptor to vet each connect / init / subscribe request (e.g., reject if token expired) ChiliCream.


### 4 Banana Cake Pop quick test

graphql
# tab 1 – subscription subscription { bookAdded { id title author } } # tab 2 – mutation mutation { addBook(title:"Dune", author:"Frank Herbert") { id } }

Tab 1 instantly receives the payload—no refresh required.


### 5 Strongly‑Typed Client Subscriptions with Strawberry Shake

Works for console, MAUI, WPF, Blazor, Unity…anything .NET Standard.

tool & package prerequisites (if you haven’t already):

bash
dotnet tool install -g StrawberryShake.Tools dotnet add package StrawberryShake.Transport.WebSockets

#### a. Tell the code‑gen to use WebSockets

Create or extend .graphqlrc.json in your client project:

json
{ "schema": "http://localhost:5001/graphql", "documents": "Operations/**/*.graphql", "extensions": { "strawberryShake": { "namespace": "BooksClient", "transportProfiles": { "Default": { "http": { "endpoint": "http://localhost:5001/graphql" }, "webSocket": { "endpoint": "ws://localhost:5001/graphql" } } } } } }

#### b. Define the operation

graphql
# Operations/BookAdded.graphql subscription BookAdded { bookAdded { id title author } }

#### c. Generate + wire up

bash
dotnet graphql generate
csharp
await using var host = Host.CreateDefaultBuilder() .ConfigureServices(s => s .AddBooksClient() // generated ext. .ConfigureWebSocketClient(c => c.Uri = new("ws://localhost:5001/graphql"))) .Build(); var client = host.Services.GetRequiredService<IBooksClient>(); await foreach (var ev in client.BookAdded.Watch()) { Console.WriteLine($"📚 {ev.Data!.BookAdded.Title} just dropped!"); }

Tip: To multiplex queries + subscriptions over the same socket, set WebSocketClientOptions.ConnectionInitializer to send your auth token during connect.


### 6 Production Checklist ✅

AreaGotchaFix
Load‑balancingSticky sessions required for WSUse Redis/Kafka back‑plane or SSE (stateless).
Firewalls / proxiesWS blockedFall back to SSE (Accept: text/event-stream).
ReconnectionsMobile devices sleepEnable Strawberry Shake’s retry/reconnect policies.
Tracing & metricsStreams can be chattyTurn on Hot Chocolate OpenTelemetry integration and sample aggressively.

### Wrap‑up

With these pieces you now have a full‑stack real‑time pipeline:

  • Server – Hot Chocolate with Redis (or in‑memory) subscriptions

  • Client – Strawberry Shake streams that deserialize directly into C# records

Add a single line of code at each mutation to eventSender.SendAsync(...) and every connected UI updates itself—no SignalR, no polling, no fuss.


Final Thoughts

GraphQL isn’t a silver bullet, but when you need:

  • chat‑style real‑time updates

  • mobile apps that loathe over‑fetching

  • complex UIs that would otherwise juggle many REST calls

…it’s a game‑changer.

With Hot Chocolate the server setup is minutes, not hours, and Strawberry Shake gives you a first‑class .NET experience on the client. Give it a try on your next side project and see how it feels—chances are you’ll never craft a fleet of brittle REST endpoints again.

Happy querying! 🚀

New Features in .Net 10

🚀 Runtime Enhancements Stack Allocation for Small Arrays The Just-In-Time (JIT) compiler now optimizes memory usage by stack-allocating s...