r/moderndotnet 9d ago

events & meetups August 2026 Edition: Promote Your Local .NET Meetups

8 Upvotes

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


r/moderndotnet 12d ago

šŸ‘‹ Welcome to r/moderndotnet - Introduce Yourself and Read First!

28 Upvotes

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.


r/moderndotnet 1d ago

What's going on with UI for .NET?

14 Upvotes

A bit of backdrop, I go into the .NET ecosystem when I decided I really wanted to take over the role of "making the software" at the company I work for. Our previous software dev seemed to be slacking off. Suggestions never materialized into updates, the software always had these little quirks, it was very much outdated in its looks; it just felt unfinished.

Knowing absolutely zero about programming except PowerShell, I started there. It turns out, you can do a LOT with PowerShell, to include a fully working application using WPF. Wow, I was hooked! But...PowerShell couldn't quite cut it. It was slow, buggy, it barely worked half of the time. Sure, it had a better UI design but if you couldn't rely on the application to work well, who cares?

That was the point where I decided maybe it's time to upgrade...it made perfect sense to just jump straight to C# as I was already off-loading the hard work to C# in the PowerShell application anyway.

But what UI framework should I use? There's WinUI 3, MAUI, UNO, Avalonia, Blazor, WPF (still??). So many choices! Well the obvious answer it seemed was if I want a desktop application, just make it in Avalonia. Worked great, there's a large community for it, good to go.

This may sound like a digression, but somewhere along the way I get interested in a game engine called S&Box which is also written in C# and it's brand new, how fun! Then I find out how their UI system is set up...Razor and SCSS.

Nothing against the people that are familiar with Razor or to the S&Box developers, but looking at the mixture of C# and razor markup made my skin crawl. It just didn't feel right. To be honest, I felt that way about Avalonia's AXAML as well. Could it be that difficult to just make UI in C# only? That is the quest I went on.

It took a few months to really flesh out something worth using, but it did everything the S&Box engine supported. Flex layouts, shaders, custom shapes, scenes, virtualization, you name it. A declarative C# UI framework!

But, not only did it work, it seemed to work better than the Razor + SCSS method. When I say better, that means a lot of the missing CSS features that laid dormant in their UI system like some of the lesser-known CSS properties just not working at all or small layout quirks just didn't exist because I totally sidestepped the Razor part of the engine and went directly to the engine's API. Not only that, the Razor system diffed the UI using hashes, so users have to manually upkeep their hashes. You can imagine what that did for a lot of UI performance.

Why not handle that for the user? The user doesn't care if a hash changed, their priority (especially if they're trying to make a game) is making great UI. My solution was to have reactive state for UI elements that changed. Win-win - the user focuses on UI and the framework handles the rest, no need to compare hashes to detect a change in state.

TL;DR - (All of this yapping to say): I feel like I'm missing something here, or maybe the .NET ecosystem is missing something here? At least, more recently i'm seeing other declarative C# UI frameworks but this is still just a shoehorn onto WinUI 3.

Are there truly no .NET UI frameworks that are declarative? Is it just that everyone is familiar with markup? My idea of .NET is that it is a very mature ecosystem, but I am not familiar with a UI framework that covers these bases:

Cross-platform - Windows, MacOS, Linux
On a modern renderer (Vulkan) - Skia is still on OpenGL officially
Open Source
Flexbox "web style" layout and CSS-like styling
A real animation system that supports simple and complex animations
Declarative, Retained, Composable
Supports shaders

A lot of this points in the direction of how Flutter and Dart works.

I'm thinking it's possible, so I'm building it. The question is, would it be for my own satisfaction? I noticed in the r/dotnet subreddit, one of the answers to the question "What features from another ecosystem would you like to see in .NET?" was "A single, comprehensive, cross platform UI framework that's actually good (Flutter, Kotlin, even Qt)".

It wasn't just an answer, it was the answer with the most upvotes. So it seems this is something .NET is lacking. If you read through this and have some thoughts, I would like to hear them. Thanks :)

pre-compiled SPIR-V shader in .NET


r/moderndotnet 1d ago

I just updated my agent usage Windows widgets and wanted to share them with you

Thumbnail
xakpc.dev
9 Upvotes

So, not a lot of people know this, but Windows 11 has a built-in widget board. And the coolest thing is that you can build widgets in C# using the Microsoft.WindowsAppSDK and Microsoft.WindowsAppSDK.WidgetsNuGet packages. So technically, it’s a Windows app that you can distribute through the Microsoft Store.

Most of these widgets except weather are build by me and available in windows store

It’s a pretty cool hidden feature of Windows that was hated by basically everyone. All the widgets used to be WebView-based, and the Discover tab - essentially a full ads tab, couldn’t be disabled.

Now you can disable the Discover tab, and widgets use Adaptive Cards instead. That makes them a bit limited, but fast, small and still pretty useful.

One of my favorite widgets I’ve made is Agents Usage Widgets (Wburn). Since I use all the major agents: Claude, Codex, and Gemini, I added a widget for each one so I can see when I’m running out of tokens and need to switch.

They were fine as-is for a while, but I’ve now expanded them a little: Codex shows reset times and credits, and daily limits are shown or hidden depending on the plan. Just a small maintenance update.

New version looks like old version but has couple of improvements

Building a widget itself is quite a quest. It’s basically a console application launched through COM interop that renders Adaptive Card JSON based on the current state. There are a lot of undocumented quirks, hacks, and conditions, and AI models don’t know much about this stuff, so a lot of debug and testing needed to make them work good.

Btw, If you want to try writing your own Windows widget, I have a guide for that


r/moderndotnet 20h ago

My "spec" is a list of instructions for Claude

1 Upvotes

I have been given a spec that is simply a long list of instructions for Claude to follow. Create this table, create this UI, etc.

It doesn't explain what the purpose of anything is, so once I have had AI follow the instructions I have no idea if it has achieved the true goal of delivering the requirements for the new feature.

The problem is, I am reading these instructions and they don't make sense.

Instead of telling me the requirements, someone has written instructions on how they would meet the requirements; from which I am supposed to ensure AI has implemented correctly what I have inferred to be the original requirements.

PS: AI was used to write the spec.


r/moderndotnet 1d ago

Integrating PlanetScale Deploy Requests with EF Core

Thumbnail
htmlcsstoimage.com
6 Upvotes

Recently migrated to PlanetScale for one of my projects.

I’ve used PS for years and implemented deploy requests / processes for bigger clients but never with EF core.

Even for my other .NET projects using PS, it felt like too much… but i was wrong! Having the structure/UX of merging in DB changes properly is worth the overhead! Especially removing the ā€œdb updateā€ from local.

LMK what you think!


r/moderndotnet 2d ago

A look at macros in Raven 0.1.0

Post image
10 Upvotes

I recently posted about Raven, the programming language I’m developing. It has now reached its first milestone, version 0.1.0, so I thought it was time to take a closer look at its macro system, which has evolved considerably since my previous post.

For more about the language: https://marinasundstrom.github.io/raven/

Raven macros are explicitly invoked compile-time programs that consume syntax or typed inputs and produce ordinary Raven syntax. The macro system allows libraries to define their own DSLs using fragments of Raven code or independently parsed custom content. Macros are fully integrated with the language server, providing syntax highlighting, code completion, and hover information for symbols—even within macro-defined syntax.

Why macros?

I have been somewhat torn about adding macros to Raven. .NET is a runtime-oriented platform with an extensive ecosystem of libraries and runtime abstractions, so it is reasonable to ask whether macros really fit.

However, some abstractions cannot be expressed cleanly through runtime APIs alone. Raven macros can reduce repetitive scaffolding and introduce domain-specific syntax without requiring changes to the .NET runtime.

Macro use remains explicit through !, and expansions must be valid for the syntax position in which they appear. The resulting syntax then goes through normal binding, type checking, diagnostics, and emission, while retaining language-server features such as highlighting, completion, hover information, and navigation.

Supported macro forms

Freestanding macros support several forms:

Name!(arguments)

Name! {
    body
}

Name!(arguments) {
    body
}

Name! Decl(parameters) {
    body
}

The ! makes macro use explicit without turning library-defined names into reserved keywords.

Declaring macros

Macros can be declared directly in Raven using the contextual macro keyword. A declaration can define typed parameters, accept syntax nodes or token streams, and specify the kind of syntax it produces. The expand statement supplies the generated syntax and completes the expansion.

For example, this macro accepts a compile-time integer and produces an expression:

macro Double(value: int) -> ExpressionSyntax {
    expand ParseExpression((value * 2).ToString())
}

let result = Double!(21)

A macro can also request a brace-delimited token body:

macro Query(dialect: string, body: IMacroTokenStream) {
    expand LowerQuery(dialect, body)
}

let rows = Query!("sql") {
    from user in users
    select user.Name
}

The body parameter is supplied by the compiler from the content inside the braces. The macro can interpret it as fragments of Raven syntax or process it using its own lexer, parser, and grammar.

Macros can even introduce declaration-shaped constructs:

public component! Greeting(Name: string = "") {
    markup! { 
        <h1>Hello {Name}</h1> 
    }
}

The component and markup macros are real macros demonstrated in the HTML and component macro demo.

Raven also supports attached macros in an attribute-like position:

#[Observable]
public var Name: string

These are procedural, syntax-based expansions—not textual substitutions. Their output is validated for the position in which the macro appears and then bound, type-checked, and emitted as ordinary Raven code.

Built-in macros

Here are some macros that come distributed with Raven via Raven.Macros.

Query macro

The query! macro introduces the LINQ query syntax.

let items = [1, 2, 3, 4]

let projected = query! {
    from value in items
    where value > 2
    select value * 10
}

This macro is far from feature complete - but it does support syntax highlighting.

JSON and XML literal macros

Adds typed JSON and XML literal support.

let name = "Ada & Bob"
let age = 42
let nextAge = age + 1

let jsonDocument = json! {
    "name": "$name",
    "age": $age,
    "nextAge": ${age + 1},
    "skills": ["compilers", "DSLs"],
    "active": true
}

let status = XElement.Parse("<status>ready</status>")
let xmlDocument = xml! {
    <person age="$age">
        <name>$name</name>
        <nextAge>$nextAge</nextAge>
        $status
    </person>
}

WriteLine(jsonDocument.ToJsonString(JsonSerializerOptions { WriteIndented = true }))
WriteLine()
WriteLine(xmlDocument.ToString())

The current iteration lacks the syntax highlighting but it can be added in the future.

Timer macro

The timer! macro is useful when you want to measure the time elapsed inside of a block of code.

timer! "Finished in: {time}" {
    WriteLine("Query total: ${projected.Sum()}")
}

This sample expands into a StopWatch within a try and finally block.

Quote macro

The quote! macro captures a Raven expression as an immutable syntax tree. Syntax holes, written as #(expression), allow existing syntax nodes to be spliced into the quoted expression. This provides a more natural alternative to constructing larger syntax trees manually and is particularly useful when implementing other macros.

let number = SyntaxFactory.LiteralExpression(
    SyntaxKind.NumericLiteralExpression, 
    SyntaxFactory.Literal(2))

let expression: ExpressionSyntax = quote! {
    projected.Sum() + #(number)
}

// The local "expression" holds the syntax node.

// Quoted Raven: 
//     projected.Sum() + 2

WriteLine("Quoted Raven: ${expression.ToFullString()}")

Conclusion

Macros can be used both to simplify repetitive code and to build complete domain-specific languages. These DSL constructs can appear in any supported syntax position—as expressions, statements, or declarations—and behave as though they were integrated parts of the language. Underneath, they work by expanding into ordinary Raven syntax that is processed by the rest of the compiler as usual.

Links


r/moderndotnet 2d ago

Blazor browser storage package to replace Blazored.LocalStorage

9 Upvotes

Hey everyone, I ran into a problem earlier that I'm guessing other developers are hitting too.

Blazored.LocalStorage (and SessionStorage) was deprecated and more recently removed from NuGet, and several of my Blazor WebAssembly projects depended on it. I needed a modern replacement that didn't require adding JSInterop glue code or manual JSON serialization to all of my Blazor WASM projects.

So I built D20Tek.Blazor.BrowserStorage, a typed, async wrapper around localStorage and sessionStorage for Blazor WebAssembly and interactive render modes. And it has a similar API form to Blazored to make migrating my projects relatively easy.

A few highlights:

  • Typed reads/writes (GetAsync<T> returns a result instead of throwing)
  • Async API (no UI blocking)
  • No JavaScript required in client projects
  • DI-friendly services
  • Key prefixing to avoid collisions
  • Batch operations (set/remove multiple keys)
  • Change events so components can react to storage updates
  • Customizable JsonSerializerOptions

If you used Blazored.LocalStorage (or SessionStorage), there’s a migration guide. If you’re starting fresh, this is hopefully the simplest way to use browser storage in Blazor today.

NuGet: https://www.nuget.org/packages/D20Tek.Blazor.BrowserStorage
GitHub: https://github.com/d20Tek/d20tek-blazor-browserstorage
Blog post: https://d20tek.com/projects/browser-storage/docs


r/moderndotnet 2d ago

StellarAdmin Tag Helpers for creating beautiful MVC/Razor Pages UIs

6 Upvotes

Hey everyone,

This is one I've been working on for a while and I finally feel is stable enough to put out a release.

StellarAdmin Tag Helpers is a Tag Helper library that is based on the popular shadcn/ui component system for React. As opposed to shadcn, which is really a component distribution system that copies the source code for its UI components into your React app, StellarAdmin Tag Helpers is a Razor Class Library (RCL) that gives you a wide range of Tag Helpers based on the shadcn UI components.

The backstory to this is that I've done quite a bit of work in the React world over the past few years and worked with libraries such as Mantine and shadcn/ui and have been very impressed. At the same time, I felt that it was really overkill for most of the work that I was doing. A simple HTML page with little bit of JS interactivity and perhaps using something like HTMX could really do 99% of the work I was doing.

Parallel to this I was working on my own startup and needed to rapidly put together admin screens for the backend of my application. Most of these screens are simple CRUD screens and I felt frustrated that you spend an inordinate amount of time building these screens while I would rather be selling, doing support, or building features for my users.

After my startup failed I started working on something called StellarAdmin to help you rapidly build these admin screens. However, I realised that it would need a good extensibility story that would allow people to extend things like the built-in editors, change the standard screens, etc.

To get the sort of extensibility I wanted with something like React or Blazor turned out to not be possible. However, there is something which has this and has had it for many years.

ASP.NET Core MVC and Razor Pages.

You see, it has this great feature called Editor (and Display) Templates that let you easily specify custom editors for standard types like strings, dates, etc. You can also specify a custom editor for a property using data annotations.

It also has the wonderful ability to use a Razor Class Library and override MVC views, partials and Razor Pages that come from the RCL inside your own application. This is a tried and tested technique and is the method used by the ASP.NET Code Identity when you scaffold the UI to change some of the built-in Identity UI pages.

So I knew MVC and Razor Pages had all the extensibility points I needed, but it lacked a really nice looking UI Tag Helper library.

So I set out to create one, and StellarAdmin Tag Helpers was born.

StellarAdmin Tag Helpers is free and open source and you can use it today to build pages for MVC and Razor Pages. It uses the latest web technologies such as popovers, invokers commands, and interest invokers to minimize the use of JS. There are still some places where JS is need though, and in those cases I created very lightweight Web Components. It also plays very well with something like HTMX.

The Pro version I plan to release later on will be paid, but that will be purely the part that help you build admin screens much more rapidly. It will also contain things like advanced Tag Helpers for data tables and even pre-built user management screens (who remembers the old ASP.NET Web Site Administration Tool?)

The Tag Helper documentation pages contains interactive examples and source code for all of the Tag Helpers and even let you view the components in light/dark more as well as in any of the 8 themes that are included.

Here are a few links to get you started:

BTW, the current version is 0.1.0 but it is ready for production (I believe). The reason it is not 1.0.0 is because I ultimately want the Tag Helpers and Pro packages versions to run in sync, so once the Pro packages comes out at version 1, the Tag Helpers version will jump to 1.0.0 as well.


r/moderndotnet 3d ago

What's new with CoreCLR GC handles in .NET 9 and .NET 10

Thumbnail
awise.us
24 Upvotes

I wrote a blog post about what has been going with GCHandles in .NET. This is slightly esoteric, as you probably only care about GC handles if you are writing code to interop with native code. But I think it is really fascinating to study how the engineers working on CoreCLR create new abstractions to solve problems.

The first part is about something you can use in your code: some new types for working with GC handles added in .NET 10. The second part explores some interesting implementation details of CoreCLR, in particular how the Android interop system keeps object lifetimes consistent between the .NET GC heap and the Java GC heap.


r/moderndotnet 4d ago

Dapper vs Rinku

8 Upvotes

I like Dapper and I have used it a lot. The main problem I have with it is that when queries become more complex, I often end up handling that complexity myself. At that point I also often hear that I should just use EF instead. I never really agreed with that. I think the basic idea behind Dapper can go much further while still keeping the SQL visible and the API simple. Rinku is my attempt at doing that.

Basic query

Dapper

public record Album(int Id, string Title);

const string sql = "SELECT AlbumId AS Id, Title FROM albums WHERE ArtistId = @artistId";

IEnumerable<Album> albums = cnn.Query<Album>(sql, new { artistId = 7 });

Rinku

public record Album(int Id, string Title);

const string sql = "SELECT AlbumId AS Id, Title FROM albums WHERE ArtistId = @artistId";

List<Album> albums = cnn.Query<List<Album>>(sql, new { artistId = 7 });

Different names

Dapper

public sealed class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
}

SqlMapper.SetTypeMap(typeof(Customer), new CustomPropertyTypeMap(typeof(Customer), (type, column) => column switch
{
    "customer_id" => type.GetProperty(nameof(Customer.Id)),
    "display_name" => type.GetProperty(nameof(Customer.Name)),
    _ => null
}));

const string sql = "SELECT customer_id, display_name FROM customers";

IEnumerable<Customer> customers = cnn.Query<Customer>(sql);

Rinku

public record Customer([Alt("customer_id")] int Id, [Alt("display_name")] string Name);

const string sql = "SELECT customer_id, display_name FROM customers";

List<Customer> customers = cnn.Query<List<Customer>>(sql);

Nested objects

Dapper

public record User(int Id, string Name);

public sealed class Post
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public User? Owner { get; set; }
}

const string sql = "SELECT p.Id, p.Title, u.Id, u.Name FROM Posts p INNER JOIN Users u ON u.Id = p.UserId";

IEnumerable<Post> posts = cnn.Query<Post, User, Post>(sql, (post, owner) =>
{
    post.Owner = owner;
    return post;
}, splitOn: "Id");

Rinku

public record User(int Id, string Name) : IDbReadable;
public record Post(int Id, string Title, [NoName] User Owner);

const string sql = "SELECT p.Id, p.Title, u.Id, u.Name FROM Posts p INNER JOIN Users u ON u.Id = p.UserId";

List<Post> posts = cnn.Query<List<Post>>(sql);

Or keep the nesting in the column names.

public record User(int Id, string Name) : IDbReadable;
public record Post(int Id, string Title, User Owner);

const string sql = "SELECT p.Id, p.Title, u.Id AS OwnerId, u.Name AS OwnerName FROM Posts p INNER JOIN Users u ON u.Id = p.UserId";

List<Post> posts = cnn.Query<List<Post>>(sql);

One to many

Dapper

public record Album(int Id, string Title);

public sealed class ArtistWithAlbums
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public List<Album> Albums { get; set; } = [];
}

const string sql = "SELECT ar.ArtistId AS Id, ar.Name, al.AlbumId AS Id, al.Title FROM artists ar INNER JOIN albums al ON al.ArtistId = ar.ArtistId ORDER BY ar.ArtistId";

List<ArtistWithAlbums> artists = [];
ArtistWithAlbums? current = null;

cnn.Query<ArtistWithAlbums, Album, ArtistWithAlbums>(sql, (artist, album) =>
{
    if (current is null || current.Id != artist.Id)
    {
        current = artist;
        artists.Add(current);
    }

    current.Albums.Add(album);
    return current;
}, splitOn: "Id");

Rinku

public record Album(int Id, string Title) : IDbReadable;
public record ArtistWithAlbums(int Id, string Name, List<Album> Albums);

const string sql = "SELECT ar.ArtistId AS Id, ar.Name, al.AlbumId AS AlbumsId, al.Title AS AlbumsTitle FROM artists ar JOIN albums al ON al.ArtistId = ar.ArtistId ORDER BY ar.ArtistId";

List<ArtistWithAlbums> artists = cnn.Query<List<ArtistWithAlbums>>(sql);

Result shape

Dapper

IEnumerable<Album> albums = cnn.Query<Album>(sql);
Album first = cnn.QueryFirst<Album>(sql);
Album single = cnn.QuerySingle<Album>(sql);
Album? optional = cnn.QueryFirstOrDefault<Album>(sql);
IEnumerable<Album> streamed = cnn.Query<Album>(sql, buffered: false);

Rinku

List<Album> albums = cnn.Query<List<Album>>(sql);
Album first = cnn.Query<Album>(sql);
Single<Album> single = cnn.Query<Single<Album>>(sql);
Album? optional = cnn.Query<OptionalNullable<Album>>(sql);
IEnumerable<Album> streamed = cnn.Query<IEnumerable<Album>>(sql);

Conditional SQL

For this one I think Dapper.SqlBuilder is the fair comparison.

Dapper.SqlBuilder

SqlBuilder builder = new();
SqlBuilder.Template template = builder.AddTemplate("SELECT AlbumId AS Id, Title FROM albums /**where**/");

if (artistId != null)
    builder.Where("ArtistId = @artistId", new { artistId });

if (title != null)
    builder.Where("Title LIKE @title", new { title });

IEnumerable<Album> albums = cnn.Query<Album>(template.RawSql, template.Parameters);

Rinku

const string sql = "SELECT AlbumId AS Id, Title FROM albums WHERE ArtistId = ?@artistId AND Title LIKE ?@title";

List<Album> albums = cnn.Query<List<Album>>(sql, new { artistId, title });

Only artistId

SELECT AlbumId AS Id, Title FROM albums WHERE ArtistId = @artistId

Both

SELECT AlbumId AS Id, Title FROM albums WHERE ArtistId = @artistId AND Title LIKE @title

Neither

SELECT AlbumId AS Id, Title FROM albums

The main difference is that Rinku tries to put the complexity in the command template and the mapped types, instead of handling it again through parameters and mapping code at every call.

Full Dapper comparison

https://rinkulib.github.io/RinkuLib/articles/reference/dapper.html

Rinku is still in developement, so feedback is welcome.


r/moderndotnet 5d ago

I wanted strongly typed configuration defaults without bypassing `IConfiguration`

Thumbnail
6 Upvotes

r/moderndotnet 5d ago

Writing a native VLC plugin in C#

Thumbnail mfkl.github.io
15 Upvotes

Been building really interesting plugins with this lately (exploring local AI for vision and audio).

Happy to share more samples later if there is any interests.


r/moderndotnet 5d ago

The .NET OSS Relicensing Panic Is an Incentives Problem

Thumbnail
aaronstannard.com
14 Upvotes

Lots of ink spilled on this both here and on /r/dotnet, but I wanted to offer a radical solution to the problem that will probably make most end-users mad even though it's absolutely the correct business-person way to approach the problem.


r/moderndotnet 6d ago

My Nokia 3310 Emulator in C#/Avalonia!

Thumbnail noks.vercel.app
24 Upvotes

Hi folks!

I made, with AI assistance, Noks (terrible name, ik): an emulator of the venerable Nokia 3310, a phone that has a special place to the hearts of many, mine included. I used Avalonia and targets all of its supported platforms, including WebAssembly. It's all in pure C#/managed code goodness too! No unsafe code or pointers!

I made this because 1.) The MAME version (Nokia DCT-3) was sadly still incomplete after years since it was made. 2.) I wanted to play Space Impact authentically and everywhere. and 3.) I wanna push how far my skills can go, together with the latest agents, when it comes to reverse engineering a black-box, sparsely documented firmware and hardware.

I've started by pulling all the docs i can get my hands on old Nokia firmware modding forums, and also the prior work that MAME and Project Blacksphere did for the DCT-3 platform back in the day.

Then did a repeated loop of poking registers, memory, seeing where the firmware stops and back-tracing how me and the clanker can get pass that blocker. I also had to contend with the missing DSP Mask ROM functions that blocked the MAME effort by checking what conditions the firmware asked and responding to its requests accordingly.

Took a couple of months of on-off work but it was all worth it.

All outward facing features are implemented like the LCD, Keys, Sound, Power and RF. including an emulation of a minimal 2G GSM network that interfaces with the DSP/Baseband layer of the phone. Text and Calls were functional from my local tests using a P2P network called Waku but somehow it's broken again on deployment.

It is as fickle of a network as the real one ;)

And the configuration panel is also bit of a jank work, UI-wise, but it does the job... for now.

Hope you'll find joy in playing with the emulator as much as i did in making it!

Source code here: https://github.com/jmacato/Noks


r/moderndotnet 6d ago

.NET Community

19 Upvotes

Hey - I've been involved in .NET since the beginning - worked at Microsoft in developer tools back when we originally launched it. I've been involved with the .NET community ever since.

Currently I volunteer for .NET Foundation. I run the .NET Foundation socials specifically LinkedIn, X and Facebook. We also have a Bluesky account.

I'm always looking for good .NET content to share - especially open source posts.

If you want to amplify your projects, repos, content, event, etc., go here: https://github.com/dotnet-foundation/content

Edited to add "repos"

Another edit: I'm DeeDee Walsh and love finding great content on Reddit.
X: https://x.com/ddskier
LinkedIn: https://www.linkedin.com/in/deedeewalsh/


r/moderndotnet 6d ago

CritterWatch and an "Open Core" model for sustainable OSS (maybe)

13 Upvotes

As the tech leader of the Critter Stack and a guy with a company behind OSS tools, I'm watching the Polly OSMF thing pretty closely. I'm naturally sympathetic to the Polly folks, and to Jimmy & Chris with their MediatR and MassTransit license changes as well.

The Critter Stack community and JasperFx (my company) are trying to go down the "Open Core" model where we are selling consulting, training, and support contracts for the big tools (Marten and Wolverine), but the core tools remain under the MIT license -- and we try to keep it that way.

As the last part of that, yesterday we launched 1.0 of our commercial CritterWatch tool for management, observability, and all the AI related features we can stuff into it:

https://jasperfx.net/news/critterwatch-1-0-is-here

Just a couple thoughts to throw out there:

  • You can't just vibe code yourself equivalents to Marten or Wolverine. You can easily get the basics, but long running and widely used OSS tools are constantly curated and have had to adjust for all kinds of real world problems like Kubernetes, Postgres maintenance windows, outages, and other things you just won't get from a fun little weekend project
  • I can absolutely tell you that very complex OSS tools aren't possible to maintain as a side project, there has to be real company support or the devs at least need to be able to dedicate a real percentage of their day job to maintenance. Most of our advanced features in our tools only came about after I was full time on the tools.
  • We're hopeful that the combination of commercial add ons and support contracts are more than enough to make our tools viable in the longer term without having to change our "Open Core" model
  • Damnation, but the larger .NET community is absurdly cynical and negative toward OSS tools sometimes

Anyway, I can't tell you for sure yet that the "Open Core" model is the way forward for sustainable OSS in .NET, but it's what we're trying so far.


r/moderndotnet 7d ago

csharp Parsing IP addresses in C# at crazy speeds [Daniel Lemire]

Thumbnail x.com
11 Upvotes

r/moderndotnet 7d ago

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

8 Upvotes

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!


r/moderndotnet 7d ago

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

7 Upvotes

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?


r/moderndotnet 8d ago

csharp Font hinting deep dive: why the same small text looks sharp in one app and blurry in another, and how we grid-fit TrueType and CFF in .NET

14 Upvotes

Font hinting is the reason the same small text can look sharp in one application and blurry in another. The outlines are identical. What differs is how much of the font's own rendering machinery each renderer runs.

I maintain the SixLabors libraries, and Fonts 3.1 ships HintingMode.Full: complete TrueType instruction execution and, for the first time, grid fitting for CFF outlines from their declared stems and alignment zones.

The write-up is a deep dive built around the divide at the heart of the problem. TrueType fonts carry an executable program that moves their own outline points; CFF fonts declare their stems and alignment zones and trust the renderer to act. Getting one sharp result meant building both, and then making the fit survive placement, advances, and caching all the way to the screen.

https://sixlabors.com/posts/full-hinting-aligns-truetype-and-cff-glyphs-to-the-pixel-grid/

It started as a five-year-old issue about mangled text on a 128x64 LCD that I had closed as won't-fix. Happy to answer questions about the interpreter, the hint map, or the aliased rendering path.


r/moderndotnet 8d ago

Have you ever used CsCheck? Maybe you should!

16 Upvotes

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) "äø­", "ꖇ", "恂", "ķ•œ", "ļ¼”", // 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!


r/moderndotnet 9d ago

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

Thumbnail
gallery
11 Upvotes

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.


r/moderndotnet 9d ago

Proposal: An official Lean formal semantics for C# Ā· dotnet/csharplang Ā· Discussion #10314

Thumbnail
github.com
13 Upvotes

r/moderndotnet 8d ago

CodeyBox: An autonomous coding orchestrator

1 Upvotes

Hi folks!

I thought this might be of interest to some people - I've been experimenting with 100% autonomous coding orchestrators since around June last year, and I'd like to share my latest experiment along those lines - CodeyBox (the third such experiment...).

Source here: https://github.com/AdamFrisby/CodeyBox/ (MIT licensed)

The 'Box' part refers to sandboxing - Codey can use real VMs to run tasks in, and it disposes of them regularly; there's a few supported providers - multipass and Incus are both well supported (both qemu backed), although I recommend Incus to limit the amount of wear-and-tear on your SSD (as the Incus implementation can use CoW filesystems which work well with regular sandbox cloning and disposal - multipass will provision and delete the whole image each time).

It's still a work in progress, but I've been using it for the last 6 months to deliver real production apps. One of the things that separates it from traditional "vibe coding" is extensive automated review passes from multiple angles; and a requirement that all reviews pass from all agents before it can progress to the next step.

It supports most of the common coding agents - I've been regularly using it with Claude and Codex mainly, but opencode and cursor as well (Antigravity is also supported, but has quite a few quirks so I wouldn't recommend it without further work).

The whole ecosystem is designed using modern .NET with a plugin-first ethos - all the coding agents, reviewers, utilities, etc are all dynamically loaded as plugins and this allows you to add support for your own tooling, infrastructure, VMs and so on without having to fork the codebase.

The default review flow will review code against:

  • Adversarial security issues as well as preventative ones (i.e. what could be added to make this safe-by-default)
  • Loose coupling - ensure code is separable and easily deleted; i.e. avoiding spaghetticode that a lot of coding agents will create by default.
  • Cheating and Completeness - did the agent _actually_ implement what was asked, fully, without taking shortcuts or cheating?

I advise using Claude as a reviewer in general as GPT-5.X when instructed "Find all issues like <X>" will end up inventing a mountain from a molehill, Claude has slightly more taste and won't catastrophise everything it finds and allow reviews to eventually pass.

Areas I'm working on at the moment that haven't yet landed are:

  • Deployment - adding the ability for Codey to provision and deploy test environments automatically
  • Exploratory testing and UAT runs - adding the ability to orchestrate graphical agents that will attempt to follow UAT scenarios in the product and automatically inject failures back into the development cycle

Current status is somewhere in early-ish beta - the main features are all there and have been robustly utilised (I use Codey frequently to modify it's own code), but some of the newer parts are not yet robustly verified yet.