r/moderndotnet

I thought desktop app development was "dead" - why so many Maui / Avalonia / Uno developers?

Consider this a "me stepping out of my distributed systems / web app bubble" question. If you casually talk on X or even at developer conferences, there's very little talk about the future of native desktop applications or even people discussing what they're building.

Yet I see tons of evidence based on the success of Avalonia and Uno that there's huge demand for technology in this area still!

What are all of these desktop app developers working on? Is it all just retro-fitting old WPF apps? What are the new ones you're building?

And where are your great conference talk submissions!

reddit.com
u/Aaronontheweb — 18 hours ago

Polly's open source maintenance fee, why is it controversial?

Carl Franklin tweeted about Polly adopting the Open Source Maintenance Fee (OSFM) and people do not generally seem very happy about it. From what I understand it's only a monthly 20 USD fee for companies that make more than 20,000 USD in revenue using at least one product or project that uses Polly.

Given the other, more dramatic monetization decisions we've seen in the past (Moq, MediatR, MassTransit), this maintenance fee seems like a pretty reasonable way to fund a project that's not otherwise backed by big sponsors or companies, no?

reddit.com
u/JansthcirlU — 21 hours ago

Have you ever used CsCheck? Maybe you should!

I've been a happy FsCheck user for many years, even though I program primarily in C# and not F#. I used it both for property and model-based testing.

I'd been meaning to check out CsCheck for doing the same thing, but aimed natively at C# developers. So I gave it a try recently and liked it!

If you are not familiar with model / property-based testing, I wrote some blog posts on this ~10 years ago using FsCheck with C# Writing Better Tests Than Humans Can Part 1 Part 2 - but the basic idea is you can assert that a property or a model holds true across a randomly generated set of inputs.

Effectively the property-based testing framework generates hundreds or thousands of random tests to exercise that these properties hold true - take for instance the double-buffering system we use for doing TUI rendering in Termina:

[Fact]
public void IdenticalBuffers_ProduceNoChanges() =>
    // If nothing changed, the diff must be empty. A false positive here would redraw the whole
    // screen every frame and bring back the flicker the diff engine removes.
    (from w in Gen.Int[1, 8] from h in Gen.Int[1, 6] from a in CellGen.Array[w * h] select (w, h, a)).Sample(t =>
    {
        var (w, h, a) = t;
        var buf = Build(w, h, a);
        var copy = new FrameBuffer(w, h);
        copy.CopyFrom(buf);
        Assert.Empty(buf.GetChangedCells(copy));
        Assert.Empty(buf.GetChangedRuns(copy));
    }, iter: Iter);

I don't show the full code from this snippet, but we generate a range of random inputs and then assert that an identical copy of the random input always produces a no-op inside the double buffer diffing system - therefore, no cells should require an update and the screen doesn't require a re-render.

We can use CsCheck to do fancier things than testing for a no-op - here's another example:

    private static readonly string[] CellPalette =
    {
        "a", "B", "7", "#", " ", "z",              // narrow (1 column)
        "中", "文", "あ", "한", "A",                // wide (2 columns)
        Cp(0x65, 0x0301), Cp(0x6F, 0x0308),        // base + combining mark (1 column; the mark is 0)
        Cp(0x1F600), Cp(0x1F389), Cp(0x20000),     // supplementary (surrogate pairs)
        Cp(0x2600, 0xFE0F), Cp(0x270B, 0xFE0F),    // emoji + variation selector (2 columns)
        Cp(0x31, 0xFE0F, 0x20E3),                  // keycap sequence (2 columns)
    };


    // A text built by joining 0..8 whole cells. Boundaries are clean by construction.
    private static readonly Gen<string> CellText =
        Gen.OneOfConst(CellPalette).List[0, 8].Select(parts => string.Concat(parts));


    // A hostile UTF-16 code unit: arbitrary chars, plus specific escapes, controls, selectors, and
    // both halves of surrogate pairs (so lone, unpaired surrogates appear too).
    private static readonly Gen<char> FuzzChar = Gen.OneOf(
        Gen.Char,
        Gen.OneOfConst(Esc, '[', ']', Bel, 'm', '\n', '\t', '\r', '\0', ' ', 'a', Cjk, Vs16, Keycap, Zwj),
        Gen.OneOfConst('\uD83D', '\uDE00', '\uD800', '\uDBFF', '\uDC00', '\uDFFF'));


    // A text of 0..24 hostile code units. May contain ill-formed UTF-16.
    private static readonly Gen<string> FuzzText =
        FuzzChar.Array[0, 24].Select(chars => new string(chars));


    // Either kind of text.
    private static readonly Gen<string> AnyText = Gen.OneOf(CellText, FuzzText);

A Gen is a generator for some random data - and it has some important properties: namely that in a more complex model based test we can reduce complex test cases to their smallest possible reproduction. So these aren't just wrappers around Random, there's more to it than that - as Anthony Lloyd (the author) explains: https://github.com/AnthonyLloyd/CsCheck/blob/master/Comparison.md#integrated-shrinking

These are data sources for tests aimed at character / text rendering. Some unicode characters in Chinese languages actually use 2x the rendering width and we'd had bugs reported related to this before. So, we can create some custom Gen data sources that will use some of these characters as random inputs.

We can then feed this into a test:

[Fact]
public void A2_CellWidth_IsZeroOneOrTwo() =>
        // A terminal cell is 0, 1, or 2 columns. A value outside that range means a glyph that
        // cannot be placed, so later column math (wrapping, cursor) would be wrong.
        AnyText.Sample(s =>
        {
            foreach (var c in DisplayWidth.EnumerateCells(s))
                Assert.InRange(c.ColumnWidth, 0, 2);
        }, iter: Iter);

In this case we assert that the DisplayWidth correctly computes that any character in the universal set of chars can only have a width of 0,1, or 2. This includes some of the hostile characters and escape codes that are lumped inside the AnyText generator.

Now that LLMs are generating a substantial portion of all new code, it's equally important that we have stronger tools to test and verify its correctness. Property and model-based testing tools like CsCheck are more than up to the task. You should give them a try!

reddit.com
u/Aaronontheweb — 2 days ago

OfficeIMO - Word, Excel, Pdf, Markdown, Email, PowerPoint etc

Hi,

I saw this new community mentioned on X and thought I'd try my luck here and see if there are people interested in parts of my project to gather feedback and potentially find people that have similar interests.

About four years ago I started building a .NET library for working with Word documents (OfficeIMO.Word). I originally maintained the DocX project before it was taken over by Xceed, so I already had some experience in that area.

I originally wrote this mostly for PowerShell users and for my project PSWriteOffice. Trying to combine ClosedXML, ShapeCrawler, OfficeIMO.Word, Sep, Sylvan and a bunch of other libraries into one PowerShell module quickly becomes dependency drama.

So the original goal was much simpler: have one set of compatible libraries covering the formats I needed. It got slightly out of hand since then, mainly thanks to Codex.

OfficeIMO is now a group of .NET libraries for creating, reading, editing, converting and rendering document formats.

It is split into focused NuGet packages, so you install the formats and converters you actually need rather than one enormous package. There are now around 100 projects/packages as part of OfficeIMO.

The current repository covers Word, Excel, PowerPoint, PDF, HTML, Markdown, RTF, OpenDocument, OneNote, Visio, CSV, AsciiDoc, LaTeX, EPUB and several older Office formats.

It also has support for email and related formats/stores including EML, MSG, OFT, TNEF, mbox, PST, OST, OLM, EMLX and Outlook OAB.

Some formats have full authoring and editing APIs, while others are mainly readers or converters.

I try to be clear about that rather than putting the same "supported" label on everything. There are still plenty of missing features and things that may be off, especially in more complicated conversions.

The conversion list is quite long, but the main parts are:

  • Word (DOCX, DOC, etc.) can be converted to and from HTML, Markdown, RTF and ODT. It can also be saved as PDF or images.
  • Excel (XLSX, XLS, XLSB, etc.) can be converted to and from HTML, CSV and ODS. Workbooks, worksheets and ranges can be saved as PDF, PNG, JPEG, TIFF, WebP or SVG.
  • PowerPoint can be converted to and from HTML and ODP. Presentations can be saved as PDF, and slides can be exported as images.
  • Markdown can be converted to and from HTML, RTF, AsciiDoc and LaTeX, and saved as PDF.
  • HTML can be converted to Markdown, RTF, Word, Excel or PowerPoint, and rendered as PDF, PNG, JPEG, TIFF, WebP or SVG.
  • OpenDocument, RTF, OneNote, Visio, EPUB and MHTML also have PDF, HTML or image conversion options depending on the format.
  • PDF pages can be rendered directly to PNG, JPEG, TIFF, WebP or SVG.
  • PDF can also be converted into Word, Excel, PowerPoint, HTML, RTF, ODT, ODS or ODP. These conversions produce editable content where possible and include a report when something could not be carried over.

OfficeIMO also has its own PDF API for creating, reading and modifying PDFs.

It supports text and image extraction, merging, splitting, page reordering, rotation, forms, annotations, attachments, encryption, signatures, redaction, optimization and image rendering.

Since I wrote this mostly with PowerShell users in mind, dependencies are intentionally limited:

  • Word, Excel and PowerPoint use the Open XML SDK for the underlying package format. Legacy binary formats such as .doc, .xls and .ppt are implemented directly without another document library.
  • HTML uses AngleSharp and AngleSharp.Css for parsing HTML and CSS.
  • Visio uses System.IO.Packaging and nothing else.
  • The optional security package uses Bouncy Castle for CMS, X.509 and timestamp-related functionality.

OfficeIMO does not use Microsoft Office or COM automation. It does not start LibreOffice in the background, and HTML conversion does not launch Chromium or another browser process. There is optional Playwright integration if you want to convert a random website to PDF and further play with PDF, but that is explicit opt-in.

The PDF parser, writer and renderer are implemented in OfficeIMO rather than wrapping a third-party PDF engine.

The same applies to the RTF, OpenDocument, Markdown, OneNote, AsciiDoc, LaTeX, CSV, EPUB and legacy Office implementations.

There is also OfficeIMO.Reader, which is basically my C# alternative to MarkItDown. It sits on top of the OfficeIMO libraries, reads all the supported formats through one API, and gives you either structured objects or Markdown output.

If you work with documents in .NET, I'd be interested to hear what you currently use, which formats or conversions give you the most trouble, and what would be useful for me to improve, add or fix long term. Maybe even what other formats should it support, including the legacy ones that are still being in use.

While I started with a much simpler goal for my PowerShell community, my end goal now is basically Aspose Total, but free, open source and MIT licensed with low dependencies.

u/MadBoyEvo — 2 days ago

August 2026 Edition: Promote Your Local .NET Meetups

Promote your local .NET user group / meetups here.

Please include:

  • Location and Time
  • Topic
  • Link to the specific event
  • Anything else that would be great for attendees to know

You do not need to be the organizer of the meetup, just an enthusiast!

Also, if you need help launching a local .NET Meetup this is one of the things the .NET Foundation can help with! Please see .NET Meetups @ .NET Foundation

reddit.com
u/Aaronontheweb — 3 days ago

Techniques for getting LLMs to produce better .NET programs

I've been AI-pilled really since the beginning of 2025 when I first gave Cursor a try, and it's been an intense, strange journey ever since.

I've written some blog posts about my experience with LLM coding that I'll link to in the comments, but I wanted to venture out and ask developers on here - what's been some techniques that have helped you get better .NET output / programs from large language models?

reddit.com
u/Aaronontheweb — 4 days ago

Building a Distributed Job Scheduler with Akka.NET

I wrote a blog post / YouTube video / OSS code sample at the very end of July to cover a scenario that one of our users ran into building a distributed job scheduler that can distribute, long-running, data-intensive jobs across an auto-scalable pool of worker processes without starting / stopping in-progress jobs as the pool grows during peak demand.

That latter part, "not rebalancing in-progress jobs," is what eliminates a lot of off-the-shelf distribution strategies like the types implemented by Microsoft Orleans and Akka.NET's Akka.Cluster.Sharding from consideration. Those frameworks are really aimed at distributing stateful "entities" - actors with important business state that live forever (often, but not always) are only intermittently busy in short bursts.

Distributing a "job" is a very different type of workload: these are tasks with a finite, well-defined lifespan in which they are intensely busy from beginning to end. Re-distributing a 10 minute job when it's 8 minutes into execution turns these into 18, 20 minute jobs potentially depending on a bunch of factors (can the job be started immediately?)

Earlier in my career I used some Akka.NET and Akka.Cluster primitives to solve this exact type of problem in the banking industry: running bank CFO "asset line management" jobs all in the final 48 hours of the month in order to meet the monthly reporting requirements and have enough data to actually complete them.

The basic formula, which I expand on in the post with code samples:

  1. Establish the ability to "size" jobs early - how many units of compute is Job A relative to Job B? This is a lot easier to define than it sounds. If you're doing asset line management, your "size" is typically the total number of assets that need to be analyzed (i.e. rows.) If you're doing call transcription it might be the size of the audio file. This should be a O(1) operation.
  2. Create a Cluster singleton (1 instance globally) who is responsible for: 2a. Managing and persisting the queue of jobs-to-be-done AND the parties who own them 2b. Subscribing to live Akka.Cluster topology update events (nodes joining, leaving, or having trouble) - this impacts our distribution system. 2c. Persisting the snapshot of which worker nodes are running which jobs 2d. Tracking progress updates across these jobs + reporting that to original callers 2e. Re-constituting all of this state after a restart using Akka.Persistence
  3. Have job receivers running on each node responsible for receiving the "job definition" and transforming that into a live execution.
  4. Have the job executors report progress back to the tracker (our singleton)

The distributed systems space tends to get dominated by stateful entity type-work, but running a large number of concurrent "jobs" is an equally tricky and nuanced space so I thought it merited some attention as well as some productionization details that might not be obvious!

Post: "Building a Distributed Job Scheduler with Akka.NET"

Repo: https://github.com/Aaronontheweb/akka.net-custom-job-scheduling

Video: Video: Building a Distributed Job Execution Platform with Akka.NET

u/Aaronontheweb — 3 days ago

Raven — is this the Kotlin moment for .NET?

I had some help from AI putting this post together and organizing my thoughts.

TL;DR: I've been building Raven, a modern programming language for .NET. It combines familiar .NET semantics and interoperability with ideas from languages such as Swift, Kotlin and Rust: unions and pattern matching, Option/Result, propagation, expression-oriented control flow, macros for building DSLs, and more.

It now has a browser playground, an SDK/compiler distribution, a language server, and a VS Code extension.

Playground (with samples): https://marinasundstrom.github.io/raven/playground/

Latest preview: https://github.com/marinasundstrom/raven/releases/tag/v0.1.0-preview.10

Background

For the last couple of years I've been building my own compiler and programming language, mainly for my own amusement. This isn't my first venture into compiler construction, but it has probably been the most creative and fulfilling one.

Raven started out much closer to C#, but gradually developed its own identity as I explored other languages and different approaches to language design. I never wanted to make "C# with different syntax," nor simply copy another language.

What emerged is something that feels at home on .NET, but with a syntax somewhat reminiscent of Swift and ideas influenced by Kotlin and Rust.

A lot has been tried and discarded along the way. I had an early implementation of union types before eventually aligning Raven with the nominal union model being introduced in C#/.NET — and then taking that model further. I experimented with trailing blocks before eventually removing them in favor of a macro system for DSLs. Error handling evolved toward Result and Option, while still retaining pragmatic interoperability with .NET exceptions and nullability.

The compiler itself uses a Roslyn-like compiler-as-a-service architecture. If you've worked with the C# compiler APIs, much of it should feel surprisingly familiar: immutable syntax trees, compilations, symbols, semantic models, and an Operations API providing a higher-level semantic representation.

Raven primarily targets .NET 11, while also supporting .NET 10.

AI has also had a significant role in the development process. Initially I mostly used it for research and finding examples. Over time I moved toward using coding agents extensively for implementation. That has made it possible to iterate unusually quickly, including making large architectural changes while simultaneously building out automated tests and custom compiler debugging infrastructure.

For the last few weeks Raven has had a playground running the compiler entirely in the browser through WebAssembly. Now there is finally a distributable SDK containing the compiler and language server, together with a VS Code extension.

Raven has grown broad enough that it's difficult to represent the language with one clever code sample, so instead I'll start with some of the fundamentals.

Hello, world

HelloWorld.rvn:

import System.Console.*

func Main() {
    WriteLine("Hello, from Raven!")
}

Like C#, Raven supports global imports, so commonly used .NET namespaces can already be available without explicitly importing them.

The syntax is different, but this is still very much a .NET language. Raven consumes .NET libraries and types directly rather than building a separate ecosystem alongside them.

Language reference: https://marinasundstrom.github.io/raven/lang/spec/index.html

Lexical bindings

Raven uses immutable bindings by default. Values declared with let cannot be reassigned:

let name = "Raven"
let count = 10

When mutable state is actually needed, you opt into it using var

var count = 0
count = count + 1

Types are normally inferred, but can also be specified explicitly:

let name: string = "Raven"
var count: int = 0

This distinction also carries into pattern matching and other language constructs: let means binding a value, rather than declaring a mutable variable.

Functions

Raven also supports namespace-scoped functions. Functions don't need to be declared as static members of a class:

namespace Inventory

func CalculateTotal(quantity: int, price: decimal) -> decimal {
    return quantity * price
}

They are ordinary namespace members and can form part of an assembly's API just like types. As with other namespace-level declarations, they are internal by default and can explicitly be made public:

public func CalculateTotal(quantity: int, price: decimal) -> decimal {
    return quantity * price
}

Option, Result and propagation

Raven has built-in Option<T> and Result<T, E> unions for modeling optionality and operations that can fail.

Raven does not pretend that null or exceptions don't exist. It has a unified nullability model and supports exceptions where appropriate, particularly for .NET interoperability. Option and Result are additional tools for cases where absence or failure are part of the domain model.

For example:

func ReserveSeats(requested: int, available: int) -&gt; Result&lt;int, string&gt; {
    if requested &lt;= 0 {
        return Error("Choose at least one seat")
    }

    if requested &gt; available {
        return Error("Only $available seats remain")
    }

    return Ok(requested)
}

func PriceBooking(
    requested: int,
    available: int,
    pricePerSeat: int
) -&gt; Result&lt;int, string&gt; {
    let seats = ReserveSeats(requested, available)?
    return Ok(seats * pricePerSeat)
}

match PriceBooking(requested: 3, available: 5, pricePerSeat: 40) {
    Ok(let total) =&gt;
        Console.WriteLine("Booking total: $total credits")

    Error(let message) =&gt;
        Console.WriteLine("Problem: $message")
}

The postfix ? propagates the failure while extracting the successful value.

This isn't hard-coded specifically to Result, either. Raven has a propagation contract, so custom types can participate in the same mechanism, including conversion between compatible residual/error types.

Unions and domain modeling

You can define your own unions:

union StockError {
    case UnknownSku(sku: string)
    case InsufficientStock(
        sku: string,
        requested: int,
        available: int
    )
}

Cases can carry data and participate directly in pattern matching.
Raven also supports the more explicit form:

union StockError(UnknownSku | InsufficientStock)

where the variants are separately declared records:

 record UnknownSku(val Sku: string)

 record InsufficientStock(
     val Sku: string
     val Requested: int
     val Available: int
 )

One of Raven's main design goals is making this kind of domain modeling natural rather than treating unions as an isolated pattern-matching feature.

Raven also supports closed (sealed) class hierarchies, providing another way to model a closed set of alternatives while retaining class inheritance.

Statements and expressions

Many of Raven's common control-flow constructs have both statement and expression forms. You can use them for ordinary control flow, or use the value they produce directly.

For example, if can be used as a statement

if temperature &gt; 25 {
    Console.WriteLine("It's warm")
} else {
    Console.WriteLine("It's cold")
}

or as an expression:

let description =
    if temperature &gt; 25 { "warm" }
    else { "cold" }

The same idea applies to match:

let message = match result {
    Ok(let value) =&gt; "Received $value"
    Error(let error) =&gt; "Failed: $error"
}

This is part of a broader design choice in Raven: control flow shouldn't require a completely different construct just because you want to produce a value from it.

Raven also provides pattern-oriented forms such as if let and let else for cases where control flow and destructuring naturally belong together.

func FindFirstEven(numbers: int[]) -&gt; Option&lt;int&gt; {
    for number in numbers {
        if number % 2 == 0 {
            return Some(number)
        }
    }

    return None
}


func DescribeFirstEven(numbers: int[]) -&gt; string {
    let Some(number) = FindFirstEven(numbers) else {
        return "No even number found"
    }

    return "The first even number is $number"
}


Console.WriteLine(DescribeFirstEven([1, 3, 8, 13]))

Visibility

You might also notice the absence of access modifiers in most examples.

Raven deliberately makes the common cases terse:

  • Type members are public by default.
  • Type members can explicitly be made private.
  • Namespace-level declarations are internal by default.
  • Declarations intended to form part of the assembly's public API are explicitly marked public.
  • So a library naturally keeps its top-level API internal until you deliberately expose it, while the members of the types you do expose don't require public everywhere.

Macros and DSLs

Another major part of Raven is its macro system.

Rather than adding specialized syntax to the language for every possible domain, Raven allows libraries and frameworks to provide domain-specific syntax through macros.

For example, Raven has an HTML macro that can be used when building Blazor applications:

Html! {
    &lt;div class="counter"&gt;
        &lt;h1&gt;Counter&lt;/h1&gt;

        &lt;p&gt;Current count: {count}&lt;/p&gt;

        &lt;button onclick={IncrementCount}&gt;
            Click me
        &lt;/button&gt;
    &lt;/div&gt;
}

This isn't a separate template language bolted onto Raven. The macro is expanded by the compiler and can produce the corresponding Blazor representation.

The syntax is deliberately more JSX-like than Razor-like: when you are inside the HTML macro, you are writing HTML until you explicitly enter a Raven expression.

Try it out here: https://marinasundstrom.github.io/raven/experiments/html-macro/

Macros also integrate with the compiler infrastructure and language server, so DSLs don't have to mean giving up editor tooling.

This replaced some earlier experiments I had with special language features such as trailing blocks. I increasingly prefer keeping the core language relatively general and letting macros provide domain-specific abstractions where they make sense.

Raven beyond console applications

Raven isn't limited to small compiler demos anymore.

You can already build web applications with Raven using ASP.NET Core and Blazor. Because Raven targets .NET and consumes .NET APIs directly, the existing .NET ecosystem remains available rather than requiring Raven-specific replacements for everything.

Sample projects: https://github.com/marinasundstrom/raven/tree/main/samples/projects

At the other end of the spectrum, Raven can also target .NET nanoFramework, including its experimental generics support, which means the same language can be used for constrained embedded and IoT applications.

For example, a nanoFramework program controlling a GPIO pin looks like this:

import System.Device.Gpio.*
import System.Threading.*

func Main() {
    use gpio = GpioController()
    use led = gpio.OpenPin(25, PinMode.Output)

    loop {
        led.Write(PinValue.High)
        Thread.Sleep(500)

        led.Write(PinValue.Low)
        Thread.Sleep(500)
    }
}

That runs in a very different environment from an ASP.NET Core application, but it's still Raven.

Raven also supports Native AOT on the regular .NET target, so applications can be compiled ahead of time into native executables rather than requiring JIT compilation at runtime.

That gives Raven a fairly interesting range already:

  • regular .NET applications and libraries
  • ASP.NET Core and Blazor web applications
  • Native AOT applications
  • embedded/IoT applications through .NET nanoFramework
  • WebAssembly, which is also how the Raven playground runs the compiler itself in the browser

This is an important part of what I want Raven to be. I'm not particularly interested in creating a language that only looks nice in isolated examples. The interesting question is whether a language can make substantially different choices from C# while still taking advantage of the enormous runtime, library and tooling ecosystem that already exists around .NET.

So, a Kotlin moment for .NET?

That's increasingly how I've started thinking about the experiment.

Not as a replacement for C#. Kotlin didn't need Java to disappear to justify its existence either.

The interesting proposition is: what if you keep .NET, but change the language?
Keep the runtime. Keep the libraries. Keep NuGet. Keep ASP.NET Core and Blazor. Keep the ability to target everything from servers and WebAssembly to Native AOT and tiny embedded devices.

But rethink some of the language-level choices: make unions and pattern matching fundamental, make Option and Result natural ways of modeling absence and failure, make control flow more expression-oriented, and provide macros so that libraries can build abstractions and DSLs that don't have to become new language features.

That's the space Raven is exploring.

Website: https://marinasundstrom.github.io/raven

reddit.com
u/marna_li — 4 days ago

Announcing Mibo Framework 4.3.0

Hey there, first time posting here.

>Just in case: my name is Angel Munoz; I'm one of the 12 F# devs in the world and I dedicate my hobby time entirely to F#

Mibo is an F# code-first micro framework on top of MonoGame and Raylib.

Mibo offers abstractions to architect your games as MVU (elmish, elm architecture) programs. and now with version 4.3.0, you can opt in for an Adaptive model with my boringly coined SPU (State, Projection, Update) which is based on Adaptive Data for incremental computations of derived state.

>If you have some frontend background, you may have heard of Signals as a way to manage state in a reactive way

While v4.3.0 has a bunch of fixes and the main item is the Adaptive Model release A minimal game I can come up with in a short snippet could be like this:

Declaring the state of the game, what is composed of and what is going to be part of the adaptive graph

type State = {
  PaddleX: cval&lt;float32&gt;
  Ball: cval&lt;Vector2&gt;; Velocity: cval&lt;Vector2&gt;
  IsHit: aval&lt;bool&gt;; PaddleColor: aval&lt;Color&gt;
}
    
[&lt;Struct&gt;]
type Snapshot = { PaddleX: float32; Ball: Vector2; PaddleColor: Color }
    
let toSnapshot (s: State) () : Snapshot = {
  PaddleX = s.PaddleX |&gt; AVal.getValue
  Ball = s.Ball |&gt; AVal.getValue
  PaddleColor = s.PaddleColor |&gt; AVal.getValue
}

>aval: Adaptive value, read only
cval: changeable value, read and write

Please note that not everything has to be adaptive or derived state, you can store any kind of values, you own that.

Some setup functions, our main game logic and the rendering view function

let init (state: State) (ctx: AdaptiveFrameContext) : AdaptiveInit&lt;Frame&gt; =
  AdaptiveInit.ofFrameBuilder(toSnapshot world)
      
let update (state: State) (_: AdaptiveContext) (gameTime: GameTime) =
  let dt = float32 gameTime.ElapsedGameTime.TotalSeconds
      
  if Raylib.IsKeyDown KeyboardKey.Left then s.PaddleX.Set(s.PaddleX.Value - 450f * dt)
  if Raylib.IsKeyDown KeyboardKey.Right then s.PaddleX.Set(s.PaddleX.Value + 450f * dt)
    
  let velocity = s.Velocity |&gt; AVal.getValue
  let ball = s.Ball |&gt; AVal.getValue
      
  let pos = ball + velocity  * dt
    
  let xVel =
    if pos.X &lt; 0f || pos.X &gt; 780f then -velocity.X else velocity.X
  let yVel = 
    if pos.Y &lt; 0f || (s.IsHit |&gt; AVal.getValue) then -velocity.Y else velocity.Y
    
  s.Ball.Set pos
  s.Velocity.Set(Vector2(xVel, yVel))
    
let view (_: GameContext) (snapshot: Snapshot) (buffer: RenderBuffer2D) =
  buf
    .fillRect(sn.PaddleX, 520f, 80f, 16f, sn.PaddleColor)
    .fillRect(sn.Ball.X, sn.Ball.Y, 16f, 16f, Color.Red)
    .drop()

Our state should be created once, the derived state will change and be tracked automatically from the adaptive state via transformations (linq style)

let state =
  let px = CVal.create 360f
  let ball = CVal.create (Vector2(400f, 100f))
  let vel = CVal.create (Vector2(250f, 250f))
    
  // Projection 1: Position collision predicate
  let isHit =
    AVal.map2
      (fun x b -&gt; b.Y &gt;= 500f &amp;&amp; b.X &gt;= x &amp;&amp; b.X &lt;= x + 80f)
      px
      ball
    
  // Projection 2: Visual feedback derived from collision state
  let color =
    isHit
    |&gt; AVal.map (fun hit -&gt;
      if hit then Color.Green else Color.White
    )
    
  { 
    PaddleX = px
    Ball = ball
    Velocity = vel
    IsHit = isHit
    PaddleColor = color
  }

bring them all together into the entry point

[&lt;EntryPoint&gt;]
let main _ =
  let program =
    AdaptiveProgram.mkProgram (init world) (update world)
    |&gt; AdaptiveProgram.withConfig(GameConfig.withTitle "Mibo Game")
    |&gt; AdaptiveProgram.withRenderer(fun () -&gt; Renderer2D.create view)
    
  let game = new AdaptiveRaylibGame&lt;Frame&gt;(program)
  game.Run()
  0

The video in the post is a sample made using adaptive state

You can find the source code for that sample here: https://github.com/AngelMunoz/Mibo.Samples/tree/master/Defli3D

If you're a numbers person you can find some numbers I tracked via the dotnet trace tool when on very busy moments of the game.

The library (based on FSharp.Data.Adaptive) is built for tight-loop work:

  • Steady state allocates nothing. Once your graph has settled, reads, writes, and delta propagation don't allocate. The exceptions are the deliberate materializations (forcetoSettoMap).
  • A value recomputes at most once per change. Ten writes between two reads cost one recompute. A read when nothing changed is a cheap O(1) check.

So... in summary this release opens up a different functional approach to mutable state which is often friendlier to high performance shaped code (rather than the traditional functional-ish looking F# code)

If you're interested to see some particular kind of genere or approach to all of this (or the more functional version MVU) feel free to let me know. I tried to make sure to open the path for F# high-performance code with some friendly APIs to ease up game development

u/Tunaxor — 3 days ago
▲ 7 r/moderndotnet+2 crossposts

I built Rinku, a micro-ORM focused on clean mapping and dynamic queries

The Rinku NuGet is a micro-ORM built directly on top of ADO.NET. The core philosophy is strictly SQL-first. The goal is giving you deterministic execution and total control over your queries while keeping the mapping and execution pipeline fully customizable and extensible.

Nested Object Mapping

C#

public record Artist(int Id, string Name) : IDbReadable;
public record Album(int Id, string Title, Artist Artist);

static readonly QueryCommand GetAlbums = new(
    "SELECT AlbumId AS Id, Title, ArtistId, ArtistName FROM albums");

// Automatically matches ArtistId -&gt; Artist.Id and ArtistName -&gt; Artist.Name
List&lt;Album&gt; albums = GetAlbums.Query&lt;List&lt;Album&gt;&gt;(cnn);

The engine resolves the hierarchy automatically without requiring attributes, configuration or manual mapping code.

Conditional SQL

C#

static readonly QueryCommand Search = new(
    "SELECT TrackId AS Id, Name FROM tracks WHERE AlbumId = ?@albumId AND GenreId IN (?@genreIds_X)");

// If albumId is passed alone, the engine cleanly drops "AND GenreId IN(...)" from the executed SQL
Search.Query&lt;List&lt;Track&gt;&gt;(cnn, new { albumId = 1 });

// If a collection is passed, ?@genreIds_X automatically expands into (@genreIds_1, u/genreIds_2...)
Search.Query&lt;List&lt;Track&gt;&gt;(cnn, new { genreIds = new[] { 1, 2, 3 } });

The command adapts to the parameters provided at execution time, expanding collections when needed and removing unused sections cleanly.

Beyond these examples, Rinku provides an extensible mapping and execution pipeline. The default behavior covers common cases, while custom parsers, dynamic objects, result handling and other parts of the pipeline can be adapted when needed. Conditional SQL also extends beyond optional filters, allowing dynamic sections throughout your queries.

Documentation and architecture
https://rinkulib.github.io/RinkuLib

GitHub repo
https://github.com/RinkuLib/RinkuLib

NuGet
https://www.nuget.org/packages/Rinku

Open to any feedback, critique or edge cases I might have missed.

reddit.com
u/Bobamoss — 5 days ago

👋 Welcome to r/moderndotnet - Introduce Yourself and Read First!

Hey everyone! I'm u/Aaronontheweb, a founding moderator of r/moderndotnet.

I started this sub because I think the .NET community deserves better than what Reddit's been giving it.

Let's be honest about why this place exists. The main .NET sub has become a place where serious technical discussions get buried or downvoted to zero, the moderation is either asleep or enforcing rules that make no sense, and a lot of good developers have just stopped posting altogether.

If you want to blanket downvote or flame someone because you don't like the Magic the Gathering game someone made with WinForms or because you're mad that someone didn't follow double-upside down hexagonal ports-and-adapters DDD clean code enterprise ASP .NET Core template or whatever, you should continue participating over there.

This is the alternative.

What we're about:

  • Real technical discussion - you do not have to adhere to Microsoft orthodoxy here. You want to implement your own GC, ship third party OSS software, or *gasp* - do things in F#, you are welcome to discuss that here.
  • Civility, enforced - if your comments sound in any way reminiscent of a Stack Overflow moderator, you will be shown the door. Disagree all you want, but don't be a prick.
  • Helpful, not hostile - beginner questions are fine.
  • Actual effort - AI slop will be auto-modded and blocked. No one wants to read something you didn't put the time into writing yourself. GitHub repositories that aren't at least 90 days old will be blocked.

What to Post
Some ideas on what to post here:

  • Blog posts and projects you've been working on (within reason) - you want to share a project you've worked on or a blog post you wrote? As long as it looks like there has been (1) sufficient human effort and (2) sufficient prior participation in this sub by the OP, it will be tolerated. Don't overdo it though.
  • Questions about .NET patterns, practices, libraries, etc - great, we love these.
  • Benchmarks, success / failure stories, and real-world experiences
  • Notes about AI and .NET - not promoting general purpose AI tools, but things _specific_ to .NET and .NET experiences.
  • Announcements about new releases, updates - again, keep it within reason just like blog posts. Not every tiddly-wink release needs to be announced on here.

Community Vibe
Don't be a jerk. Stay on-topic. Don't post AI slop (not the same as posting about AI, which is fine); and have fun.

How to Get Started

  1. Introduce yourself in the comments below.
  2. Post something today! Even a simple question can spark a great conversation.
  3. If you know someone who would love this community, invite them to join.
  4. Interested in helping out? We're always looking for new moderators, so feel free to reach out to me to apply.

We're starting from ground zero - please help build a better culture than the other sub. Invite the .NET devs you actually respect. And if you want to help moderate, message me — we're looking for people who care enough to show up.

Welcome to the modern .NET community.

reddit.com
u/Aaronontheweb — 6 days ago

GitHub - davidwhitney/JsxCore: Native support for JSX and TSX as a server and client rendered view engine for ASP.NET Core. Bringing the modern web to ASP.NET.

JsxCore brings native JSX support (React, Preact) to ASP.NET MVC and Minimal API as an ASP.NET view engine.

Another in a series of "can we converge modern web dev into the .NET ecosystem" projects that I'm working on.

So, Blazor is cool - but it's absolutely swimming against the current of the entire rest of the web development ecosystem. As it turns out, functional reactive programming is a pretty nice model for rich client applications and because of that React has totally dominated the space for a decade. There are probably more "React only" developers than most other programming languages have users.

This puts us in a weird spot in the .NET ecosystem - all the cool stuff in modern web is happening elsewhere, in a framework that's native to Node. This project is my attempt to pull the two ecosystems together so you can write idiomatic .NET in what I believe to be the best server side framework in the world, and use the same modern tooling that everyone else has for your UI.

Highlights:

- Doesn't require node! At all! (there's a home rolled NPM client to deal with dependencies from NPM so you can build your projects entirely with only dotnet in your path)
- Supports HMR, ESM, and a bunch of other goodies
- Shells out to esbuild for production style processing
- As a side-effect of using the native TypeScript compilers, adds native, toolchain free TypeScript first class support to your MVC apps - just import a TypeScript file as a module and it just works
- Generates TypeScript side type definitions for viewmodels you return to your views
- Generally just works as a drop in replacement for Razor views (.cshtml) in your apps
- Supports server side rendering with client side hydration via the JINT library

And before the "why not Blazor!" folks ask - because I think React (and the bundled Preact) is a pretty awesome way to build UI that doesn't force you over a compatibility WASM bridge which introduces a bunch of friction for front end devs.

I imagine this tool will be well suited for teams where there are split frontend and backend job roles working in the same app, that want to avoid the abject pain and misery of setting up a backend, a frontend, configuring reverse proxies to join em up etc etc that making ANCM and React play nicely together previously required.

github.com
u/davidwhitney — 4 days ago

Avalonia Updates

All our posts to the dotnet sub were removed as self-promotion, so you may have missed some big developments with Avalonia this year!

Avalonia 12.0

- Major rendering and compositor performance overhaul, with dramatically better performance in complex visual trees.
- Compiled bindings enabled by default.
- Major Android performance improvements, including faster startup, smoother scrolling, lower CPU usage and a native Android dispatcher.
- Native Linux accessibility via AT-SPI2, including screen-reader support.
- New page/navigation model with ContentPage, DrawerPage, CarouselPage, TabbedPage/TabView and PipsPager.
- WebView became fully open source.
- New themeable client-side window decorations.
- Expanded Dispatcher APIs, including CurrentDispatcher, FromThread, Yield and Resume.
- Major focus-management overhaul, including cancellable focus changes and custom traversal.
- Mac Catalyst support and modern iOS scene lifecycle support.
- New macOS Dock menu API and assorted Windows interoperability improvements.
- Moved to .NET 10 and SkiaSharp 3; removed Direct2D1, Tizen, Browser.Blazor and most netstandard2.0 support.
- Large number of performance, memory, virtualisation, rendering and platform-specific fixes.

Avalonia 12.1

- Native Wayland backend, removing the dependency on XWayland. Currently experimental and opt-in.
- Full cross-application drag-and-drop on X11 via XDND.
- X11 and alternative Windows renderers can render above 60 FPS based on monitor refresh rate.
- Further rendering optimisation, including hardware-accelerated clipping, substantially faster bitmap creation and lower compositor allocations.
- Vastly faster hit-testing for very large visual trees.
- Better NVIDIA/OpenGL support on Linux.
- New TableView control for lightweight, read-only tabular data, sitting between ListBox and DataGrid.
- Native sound and haptic feedback APIs on Android and iOS.
- New Avalonia.WinUI package for embedding Avalonia controls inside WinUI 3 applications.
- Windows 11 rounded-corner control and Android system-theme change detection.
- SBOMs now ship with Avalonia releases as part of CRA/supply-chain compliance work.

Growth

In all of 2025, we saw ~122M unique builds using Avalonia. On the first half of 2026, that number increased to over 400M.

We’re continuing to see significant adoption of the framework, which is creating a bigger, more vibrant ecosystem!

Partnerships

We’re continuing to work on an Impeller based backend for Avalonia, as we explore alternatives to SkiaSharp. We also began sponsoring the incredible work of James on ImageSharp. He’s built an alternative backend, that we’d love to see people adopt.

The option to use Avalonia as the renderer on your next MAUI project will be built into the official MAUI templates. We continue to work with the MAUI engineers, looking for mutually beneficial opportunities to push the ecosystem forward.

reddit.com
u/AvaloniaUI-Mike — 5 days ago

I made a Rider plugin for switching NuGet references to local projects

I've been working on a small Rider plugin to make working across multiple .NET repos a bit easier.

It lets you replace a NuGet PackageReference with a local ProjectReference from Rider, work against the local source, then switch it back to the original package reference when you're done. It can also scan configured source directories to find the matching project automatically.

I mainly built it because I do this fairly often and got tired of manually editing .csproj files.

It's still fairly new, so feedback/issues are welcome if anyone else has a similar workflow.

https://plugins.jetbrains.com/plugin/33455-reference-switcher/ https://github.com/tombiddulph/ReferenceSwitcher

reddit.com
u/tombiddulph — 4 days ago

Bringing C# to Astro with AstroSharp

I've been working on a few interesting projects meant to bridge the gap between modern web development and the .NET ecosystem.

Many of you might have heard of Astro - a static site generator predominantly for the TypeScript ecosystem. Astro is awesome, it's probably the best implementation of a static site generator out there and it gives you all the modern conveniences of bundles, minification and live dev server experience during build.

So why not C#?

This project is (perhaps somewhat confusingly) an npm package that allows you to build a regular Astro project using C# and Razor. It's not a reimplementation of Astro - it's still Astros routing, and web stuff, but it extends Astros regular support for React, .astro files et al to also include Razor components and Razor pages.

Razor comes with a defacto front matter (the code block at the start of the file), can mix-and-match with Astro .astro files and React server rendered components, and allows you to use anything that can execute on the server during build time.

Want more? You can also write the coded parts of your astro site as regular .cs files - so your data loaders can be written in C#, and when your Astro site is npm run build built it just works.

Under the hood, obviously this relies on .NET being in the path of the machine, and the NPM package publishes an Astro plugin that boots up a sidecar process that communicates with astro over JSON-RPC at dev and build time. It's pretty cool and seemless - uses Roslyn in memory to do hot module reloads once the sidecar is already up by silently generating C# projects in a .astrosharp file and compiling with no real perceptible difference in performance (10ms page renders or so).

There's experimental support for WASM for client rendered stuff (though I'd probably not recommend it unless the app you're building is non-trivial on the client because you buy about 1.2mb of framework stuff like a Blazor site), and slightly less experimental support for the server-side functions that Astro has introduced - again using WASM hosted inside node. This bit... seems to work... but I've not used it in anger because all the Astro projects I have are pure build-time-static generated.

First releases are here on GitHub: https://github.com/davidwhitney/astrosharp and NPM.

u/davidwhitney — 4 days ago
▲ 3 r/moderndotnet+2 crossposts

Result patters + CQRS! Just want to share wehat I did!

Hello!

As you may know( or not), I love result patterns. At some point, of course.

And I have my project https://github.com/managedcode/Communication

And yesterday I found an amazing (or you can tell me about it) idea: CQRS with IAsyncEnumerable!

You may know how annoying it is to implement CQRS properly, but but but!

What if we can call a method which will use SSE (server-sent event) protocol to send chunks, and it will be IAsyncEnumerable in your cleint.

so it's async, but you can wait for it! 

I add full impemention for server and cleint and Orleans(I love orleans)

What do you think?  Give me feedback, please!

u/csharp-agent — 5 days ago

What if dnx was its own native thing?

I wished on X that dnx was its own native AOT thing so you could use RID-specific, self-contained AOT .NET and NuGet as the distribution mechanism.

Why would such a thing be useful?

  1. No .NET (runtime or SDK) required: precisely the point of AOT'ed tools
  2. Keeping dnx ease of use: dnx <tool>[@version]
  3. Reuse massive .NET distribution channel via nuget.org, GitHub packages, or custom feeds (i.e. sleet)
  4. Ease of authoring/packing: pretty trivial with a CI workflow
  5. Ease of consumption for end users: just one small (~4MB) native tool to run them all.

Then I realized that nowadays you can just ship things you wish existed. I'm spending some quality AI tokens on the "problem" to create ndnx (native dnx). Still waiting for the winget PR to be merged so it shows up there too.

Startup times are SOOO much better too!

Obviously, I think this is something dotnet/dnx itself could/should do. Right?

Try this out today: > macOS/Linux: curl -fsSL https://github.com/devlooped/ndnx/releases/latest/download/install.sh | sh

> Windows(pwsh): irm https://github.com/devlooped/ndnx/releases/latest/download/install.ps1 | iex

Then use ndnx instead of dnx as usual. It even reuses the same package cache.

u/danielkzu — 5 days ago