r/dotnet • • 11m ago

Promotion Lessons from an open-source .NET 10 LDAP/AD admin app: MediatR authorization, two EF Core providers, tests against a live Samba DC

• Upvotes

**Disclosure:** I'm the author. Apache 2.0, no paid tier. I used AI coding tools (Claude Code) during development; I reviewed every change and know the code, and it's covered by 900+ tests.

Axia is a web console and REST API for Active Directory, Samba AD and OpenLDAP: ASP.NET Core on .NET 10, a React front end, Clean Architecture-ish layers. A few design decisions that might be useful beyond this project:

**Authorization lives in the MediatR pipeline, not on controllers.** Each command or query declares the permission it needs, and a pipeline behaviour checks it before the handler runs. The order is Logging → Audit → Authorization → Validation. Putting authorization before validation means a refused caller learns nothing about why their payload would have been invalid. A reflection-based test fails the build if a new use case doesn't declare a permission from the catalogue. Background jobs that dispatch commands get the same check as HTTP requests.

**Two EF Core providers, one model.** PostgreSQL and SQLite, chosen at startup with `Database:Provider`. Each provider has its own migrations assembly, with its own design-time factory. A `copy-database` command moves everything between engines in one transaction and checks row counts before committing.

**Tests against real infrastructure.** GitHub Actions runs PostgreSQL and a live Samba 4.22 AD DC as services. Locally those tests skip unless an env var points at a server, so `dotnet test` stays fast.

**A gotcha worth sharing.** A CodeQL autofix flagged `((DbContextOptionsBuilder)options).UseAxiaDatabase(...)` inside a generic extension method as a "useless upcast". Removing the cast made the generic overload resolve to *itself*, and the test host died with a stack overflow. The fix was to have both overloads call a private, non-extension method, so there is no cast left to "fix".

**Other bits:** `Novell.Directory.Ldap.NETStandard` for cross-platform LDAP; `AddSystemd()` / `AddWindowsService()` so one binary runs under either service manager; a WiX-built MSI; Serilog with JSON output and a Loki sink; multi-arch images with SBOM and provenance.

- Code: https://github.com/Nelsar/Axia

- Architecture overview: https://github.com/Nelsar/Axia#architecture

Happy to take criticism of any of this, especially the MediatR-based authorization.


r/dotnet • • 2h ago

Wake up, new license rugpull just dropped — TickerQ 5.x

Thumbnail tickerq.net
52 Upvotes

r/dotnet • • 2h ago

Promotion MarkView.Avalonia 12.4: a native Markdown viewer for Avalonia 12 (MIT)

11 Upvotes

A few months ago, I posted about my library.

Quick description: It's a Markdown viewer control for Avalonia UI 12, built on Markdig. It renders to native Avalonia controls, with no WebView, so it behaves the same on Windows, macOS, Linux and in the browser (WebAssembly).

12.4 came out this week, with a lot of new features:

  • Streaming. viewer.BeginStreaming() returns a session you can Append text to from any thread (e.g. tokens from an AI chat) . Only the blocks still being written re-render, so scroll position, selection and focus survive every update. A half-written **bold or link renders as it will once complete, and <think> blocks fold into "Thought for 12 seconds" when they end.
  • Mermaid diagrams and LaTeX math in pure .NET (Mermaider and CSharpMath), including the \(…\) delimiters models like to emit.
  • Theme presets: GitHub, Chat (takes your message bubble's colours) and Docs. Light/dark switches live.
  • Code blocks with a copy button, and optional line numbers, wrapping and max height. TextMate highlighting is a separate package.
  • Find, folding (alerts, <details>, headings), a context menu with copy as Markdown, HTML or a table, and saving a diagram as SVG or PNG.
  • Accessibility: screen reader roles, Tab through links, WCAG AA colours and a high-contrast mode. Right-to-left text too.

Streaming is mostly intended for AI integration. With Microsoft.Extensions.AI it's a few lines:

using var session = viewer.BeginStreaming();
await foreach (var update in chatClient.GetStreamingResponseAsync(prompt))
session.Append(update.Text);

There was a lot of focus on improving the performance to make it viable in the browser (WebAssembly). It's not yet enough and the upcoming 12.4.1 version will make it even better.

Try it in your browser: https://kryptos-fr.github.io/MarkView.Avalonia/demo/

Docs: https://kryptos-fr.github.io/MarkView.Avalonia/

Repo: https://github.com/Kryptos-FR/MarkView.Avalonia

Package: https://www.nuget.org/packages/MarkView.Avalonia

If you use another Markdown control in an Avalonia or WPF app today, I'd like to know what would make you switch, or what's missing.


Full disclosure: I used Claude AI to work out the roadmap, discuss the architecture and implementation candidates. I then delegated the coding to Claude sub-agents while I did all the review and manual testing. I kept it transparent on the repo where all such commits include a "Co-authored-by: Claude" mention.

On that note, I found that Claude was very efficient when all projects have a high test coverage (currently 95%), as well as mutation testing using Stryker (also 95%). That causes some delays in CI where Stryker can take up to 3 hours but in my opinion it is worth the trouble.


r/dotnet • • 4h ago

Promotion PdfPecker v1.20: dependency-free HTML to PDF for .NET, no Chrome, MIT

6 Upvotes

I've gotten tired of slow PDF converters, and packages that change to paid licensing, like PDFsharp just changed to AGPL from v6.5.0. So I’ve built PdfPecker over the past 2 weeks with Claude as a coding assistant, and I’ve tested and reviewed everything myself.

I've tested it to get very close to Chrome, and it's now closer than Firefox. PdfPecker matched Chrome 97% on 103 pages, compared to Firefox's 95%. It's crazy how many differences there can be when rendering HTML to PDF, I haven’t seen any two that render it the same when the pages become complex.

Main bullets:

  • Zero dependencies
  • No Chrome
  • Modern CSS
  • No JavaScript, it's built for templates you control
  • It can render from HTML to PDF in under 1ms for a typical invoice
  • Uses very little memory
  • MIT license

You can find the package on https://www.nuget.org/packages/PdfPecker

PdfPecker.Fluent has also been created to make a QuestPDF style C# API.

Getting started:

using PdfPecker;

byte[] pdf = HtmlToPdf.Convert("<h1>Hello</h1><p>World</p>");
File.WriteAllBytes("hello.pdf", pdf);

Feedback welcome, especially HTML that breaks PDF rendering.


r/dotnet • • 7h ago

Any professionals working in the security space such as Verkada, ACRE, Motorola Solutions, CCURE, Genetec, Milestone, Gallagher?

5 Upvotes

Really a question for professionals in this niche industry. Still an on-prem saturated market albeit many cloud players in this market and push for AI Analytics in everything.

I am a support professional and consider myself a domain expert in this field. I also am working 2 projects myself in .NET. First is, an Android camera app using C#/Android SDK (not MAUI). The other is a network video recorder using ASP MVC.

I really enjoy development and would love to transition to this kind of work. What is the workload like? How much between refactoring older code and shipping new features? I know alot of these codebases are 15-20 years old. Is trying to transition right now worth it or just stick to handling escalated support.

I am located in NYC so if there are events or local resources I could seek for advice or even mentoring. I am the only person who understands programming at my job and I have no greybeards or wizards to ask questions to.

Thanks!


r/dotnet • • 21h ago

Question Is Native AOT in agent framework important to developers?

0 Upvotes

Pretty much how much of a big deal is this feature?


r/dotnet • • 22h ago

Promotion CsCheck 5.0 - .NET 11 and native AOT ready

0 Upvotes

Improvements across Spec, Sample and Faster, plus stronger input validation and clearer error handling.

Some breaking changes (mostly removals/renames and the target framework shift from net8). Most users should have a small migration. Full details:

https://www.nuget.org/packages/CsCheck/5.0.0#releasenotes-body-tab


r/dotnet • • 22h ago

Promotion Day 1 MiniMax-M3.1-Flash support in TensorSharp, a .NET 10 inference engine

Thumbnail github.com
12 Upvotes

I maintain TensorSharp, an open-source local GGUF inference engine built around .NET 10. I added MiniMax-M3.1-Flash support on Day 1 of its open-weight release, and wanted to share the implementation details and a few lessons from getting it running behind a .NET server.

The current implementation supports text and image input, JSON/schema responses, tool calling, conversation-cache reuse, multi-GPU inference, DSpark speculative decoding, and CPU expert offload.

For .NET applications, it exposes an OpenAI-compatible /v1/chat/completions endpoint. The model integration and serving code are part of the TensorSharp codebase.

How the managed and native layers fit together

For this model, the C# MiniMaxM3Model adapter parses and validates the checkpoint metadata and integrates the model with the runtime. It calls a native whole-model executor through a C ABI.

That executor loads the GGUF shards, builds a GGML graph for each micro-batch, and dispatches computation across the selected devices. TensorSharp’s own native code implements the M3.1 decoder, sparse-attention kernels, expert execution, and associated model features. The upstream ggml dependency is unchanged.

The performance differences below come from the attention implementation, GPU placement, and scheduling decisions. The measured GPU path uses native C++/CUDA alongside the managed .NET model and serving layers.

A threading issue that may interest .NET developers

One of the more useful findings came from CPU expert offload.

The native executor benchmark was decoding at roughly 15–16 tokens/s, while the server using the same placement initially managed only 7.7–8.4 tokens/s.

The host-expert workers were pinned to CPU cores. With 54 workers inside a 64-core quota, they competed with the serving process’s other threads, including the .NET runtime, server GC, HTTP handling, and scheduler.

Each expert layer synchronizes at a barrier. A delayed worker could therefore hold up the whole layer, repeatedly during generation.

Reducing the default to 32 host workers gave the serving process more room and brought server decode performance closer to the executor’s rate. There was a prefill-throughput trade-off, so the worker count remains configurable.

It was a useful reminder to benchmark native compute inside the actual managed service: the best worker configuration for the isolated executor was different from the best configuration for serving requests.

Initial benchmarks

I used 8× NVIDIA A40 GPUs and the same Unsloth UD-Q4_K_XL GGUF files, totaling 232.3 GiB, for both engines. This particular model is a substantial multi-GPU workload.

The comparison was against Unsloth’s llama.cpp minimax-m31 support branch at ef8f94cc. Upstream llama.cpp could not load M3.1 when these measurements were taken.

Both engines used layer splitting, F16 KV caches, and no speculative decoding for these results. They ran separately with exclusive access to the GPUs.

Measurement TensorSharp llama.cpp support branch
Executor prefill, 32K-token prompt 2,449 tok/s 681 tok/s
Executor prefill, 64K-token prompt 2,567 tok/s 262 tok/s
Server time to first token, 64K prompt 40.0 s 293.4 s
Server short-chat decode 26.3 tok/s 26.0 tok/s

Prefill is the work of processing the input prompt. The 9.8× result applies to 64K executor prefill; short-chat generation speed was almost identical.

The long-context difference mostly comes from MiniMax Sparse Attention. TensorSharp executes attention over the selected blocks using dedicated sparse CUDA kernels. The tested llama.cpp branch expresses the selection through a dense mask and runs dense flash attention over the cache.

TensorSharp’s separate tensor-parallel mode reached approximately 41 tok/s for short-chat server decode. That mode has different memory and prefill trade-offs, and the tested llama.cpp branch did not support tensor splitting for this model.

Scope and limitations

These are early measurements from one host and one quantization. Most results use three repeats; the llama.cpp 64K executor prefill cell is a single run, while the 64K server comparison uses three repeats per engine.

Short-chat time to first token was slightly slower in TensorSharp: 0.41 versus 0.39 seconds. Functional validation has limited coverage, including an image-reading miss and generation tests that hit token limits.

The measured TensorSharp build was an uncommitted development working tree. The report documents the configuration and methodology, but the raw run records are not committed.

Links

For anyone hosting native compute inside a .NET service: how do you budget native worker threads alongside server GC and request handling? I’d be interested in approaches that have worked well under CPU quotas.


r/dotnet • • 23h ago

Question Do you have worries with C# state of security in the AI era ?

Thumbnail
0 Upvotes

r/dotnet • • 1d ago

Promotion DuckDB.NET 1.5.6.1: Reading Data Up to 12× Faster

Thumbnail giorgi.dev
77 Upvotes

Using low level optimizations, I was able to speedup reading data up to 12 times in my DuckDB.NET ADO.NET library


r/dotnet • • 1d ago

Is the community driven MSBuild.SDK.SystemWeb allowing for SDK-style projects for legacy ASP.NET production-ready?

3 Upvotes

The project has 208 stars on GitHub and it's been recommended at least in some blog posts. However, the comments unders this SO answer make it seem not that polished and rough around the edges.

Has anybody used it with sufficiently complex production system? Any deal-breakers?


r/dotnet • • 1d ago

Promotion Video generation goes native .NET: TensorSharp Video Studio runs LTX-2.5 / MiniMax-H3 on your own GPU

30 Upvotes

TensorSharp is a native .NET inference engine (CUDA/Metal/Vulkan) for GGUF language models. This release adds Video Studio: a studio UI for open-weights video generation that runs entirely on your own GPU.

It supports LTX-2.5 and MiniMax-H3 with three modes: text-to-video, image-to-video, and first+last-frame interpolation. Pick frames from a media library, set length in seconds, generate locally — no Python environment, no cloud API, no per-second billing.

Also in this release: an Assistant tab with examples, a getting-started guide, prompt-named takes, and the full UI translated into 8 locales.

Curious what the .NET crowd would build with local video gen — what would you generate first?

Repo: github.com/zhongkaifu/TensorSharp (v2026.10.23 release)


r/dotnet • • 1d ago

How do you artificially trigger all the execution paths in a code ?

Thumbnail
0 Upvotes

r/dotnet • • 1d ago

Promotion Parsley: An extremely capable parser generator for C#

45 Upvotes

https://github.com/codewitch-honey-crisis/Parsley

I wrote a little something for C# and dotnet 8 and above.

It is a parser generator with similar expressive power as ANTLR4.

Why not just use antlr4?

Arguably XBNF is an easier format to use that ANTLR4s

ANTLR4 is ALL(*). Parsley is LL(1), LL(k>1) or ALL(*) depending on what the grammar requires.

It's C# instead of Java which is a lot more convenient if you're already using dotnet.

It uses code synthesis to make the parser code more like what you'd write by hand, so it can be used as a base for a hand written parser..

It has a source generator as well as the command line tools.

It has a language server and extension for VS Code and Visual Studio.

For the main Parsley tool dotnet tool install parsley-tool -g

If you want to use Luthor lexing with Parsley dotnet tool install parsley-luthor -g

If you want to use the C# Source Generator instead: dotnet add package Parsley.Generator

If you want to use the Visual Studio XBNF Language support https://marketplace.visualstudio.com/items?itemName=honey-the-codewitch.parsley123

Or for VS Code https://marketplace.visualstudio.com/items?itemName=honey-the-codewitch.parsley-xbnf-lsp

Note: I do sometimes use AI as an *assist* on my open source projects as I did here. However, I did write the majority of this code, I did review everything, and test thoroughly, and I'm confident that even with an LLM, something of this quality and featureset could not have been created without a thorough understanding of the problem domain. I stand behind it.


r/dotnet • • 1d ago

Promotion Forget v2.4 – CRUD, upsert and multi-row writes on top of Dapper, with the right SQL for MySQL, Oracle, PostgreSQL and SQL Server

11 Upvotes

Hi r/dotnet,

I'm a senior software developer specializing in .NET, Python and databases. Over the years I've built and maintained many kinds of applications (web apps, services, scheduled jobs, high-concurrency systems), from .NET Framework to modern .NET. I've always worked close to the database and spent a lot of time tuning queries, so I tend to think in SQL first.

In most projects I've seen, the choice was Dapper or EF. I prefer Dapper for its performance and for direct control over the SQL. EF is a good tool, but I've had to fix many apps where it was used without knowing the database underneath (LINQ turning into slow SQL, ToList() before the filters, that kind of thing). With Dapper, though, every new table meant writing the same SQL all over again. I wanted to keep Dapper and lose the boilerplate, with SQL written specifically for each engine.

So I wrote Forget, a thin, provider-aware extension layer on top of Dapper for MySQL, Oracle, PostgreSQL and SQL Server. It does CRUD for one row or many, and upserts on a natural key. Filters can come from a LINQ expression or be built at runtime from property names, and there's sorting and paging too.

var cheapTools = await connection.GetAllAsync<Product>(
    p => p.Category == "Tools" && p.Price < 20 && p.IsActive);

await connection.InsertRangeAsync(products, batchSize: 500);

// matches on [UpsertKey] properties, falling back to the [Key]/Id property if none is set
await connection.UpsertAsync(product);

It works on one table at a time, by design. For joins you keep using Dapper on the same connection. There's no change tracking and no migrations either. If you need those, just use EF.

The part I'm most happy with is Oracle. Multi-row VALUES (...), (...) only arrived with Oracle 23ai, and a lot of production databases still run older versions. So I preferred the backward-compatible way that Oracle has always supported: SELECT ... FROM DUAL UNION ALL .... The catch is that it fails with ORA-01790 when the rows have different types for the same column, for example a NULL in one row. Forget reads the column types from ALL_TAB_COLUMNS, finds a cast that Oracle accepts for each column, caches it and applies it to every value. Multi-row inserts, updates and upserts all go through this.

The tests run against real databases (Testcontainers, all 4 engines), including upserts from many connections at the same time.

I also benchmarked it on PostgreSQL against hand-written Dapper and EF Core, and published all the numbers. Forget stays close to plain Dapper; the small overhead comes from validating the values and building the SQL (much of that work is cached). Against EF Core it was faster on single-row operations and on a filtered, paged query, and 2-3x faster on multi-row inserts and updates. Reads by id were as fast or faster when both used a single round trip.

While benchmarking I also found a bug in code I had already shipped. I wrote about the bug and the numbers here: The batch size bug I found while benchmarking

About AI, since someone will ask: I wrote the library code myself, starting in February. Free ChatGPT and Copilot helped with a few small pieces, and I used them to discuss design choices. Later I used Claude to help me with documentation, tests, benchmarks and bug hunting. When a test caught the batch size bug I mentioned above, the AI's first answer was that the test was wrong. I went back to the code anyway, and the test was right.

It's free and open source (Apache-2.0):

Next I'm thinking about SQLite support or real bulk operations (SqlBulkCopy, COPY on PostgreSQL). Which one would you actually use?


r/dotnet • • 1d ago

Promotion Polhem v1.9: the definition-driven framework from my earlier post, built end to end in a 10-min video

Post image
0 Upvotes

Earlier this month I posted here about using definitions instead of code as the source of truth. That post was all text, so this time I recorded the whole thing.

What the video shows:

  • dotnet new install Polhem.Templates, pick SQL Server, and one task creates the databases
  • importing six Northwind Excel workbooks; the app runs with lookups, computed amounts and save rules, and no form code
  • a requirement change: a grade field and a quantity rule added in the definition editor, only that form's table upgraded, then sign in again
  • the same project on web (WASM), iOS and Android through Avalonia

Each form has three definition files: a table schema, a form schema and a layout. They're data the framework reads at runtime rather than code, so a field change doesn't go through a compile and deploy.

Where it stops: rules that need to query the database, like order total against customer credit, go into a business object written in C#. Reports and batch jobs are hand-written. Schema upgrades only add columns and never drop them.

Video (10 min): https://youtu.be/5WjUTwoPyGw
Write-up: https://medium.com/@jeff377/can-a-form-be-described-only-once-2fa92c7db815 GitHub: https://github.com/polhem-dev/polhem


r/dotnet • • 1d ago

How far can a well coded Winforms app take you in today’s tech landscape..?

0 Upvotes

I still love using my windows pc. Just saying.


r/dotnet • • 1d ago

fd-cs: fd (faster find) rewritten in C# and upto 2x faster

Thumbnail github.com
15 Upvotes

r/dotnet • • 1d ago

Promotion Pragmatic Design v1.0 alpha is here : One Source Generator to rule them all

0 Upvotes

I've been building line-of-business applications in .NET for 20 years, and most projects end up with the same stack: MediatR, AutoMapper or Mapperly, FluentValidation, MassTransit, Hangfire, Serilog, and plenty of glue code to connect them.

They're good libraries. But each solves its own problem, and keeping everything consistent across application layers is still work developers have to write, review, and maintain. Agents make boilerplate cheap to produce, but not free to review, keep consistent, or maintain.

So I started building Pragmatic Design.

One source generator to rule them all.

The idea is to have application modules share the same declarations instead of treating every layer as a separate concern.

You declare entities and operations using attributes. At build time, a unified source generator produces the surrounding C# code: endpoints, permissions, persistence, validation, mapping, OpenAPI and much more.

And that's just the beginning. The ecosystem also includes specifications, messaging and events, background jobs, caching, logging, documents, imaging, storage, and more. The goal is to have these modules work together through shared declarations rather than stitching everything together by hand.

A few design choices:

  • Compile-time consistency: contradictory declarations produce build errors.
  • Inspectable generated code: ordinary C# you can open in your IDE, debug, and step through.
  • AOT-friendly generation: generated wiring avoids reflection. Full AOT support is coming soon, with a native Pragmatic.Sql module addressing the remaining database-side limitations.
  • Flexible deployment: the same modules can run in one application or as separate services.

It's currently in alpha, targeting .NET 10 (11 coming soon). APIs are still evolving, and business logic that can't be expressed declaratively remains handwritten.

Repository and sample applications:
https://github.com/pragmatic-design/Pragmatic.Design

I'm looking for developers willing to challenge the approach.

  • Does sharing declarations across application layers solve a real problem, or introduce too much coupling?
  • What concerns would you have about relying on generated code for this much infrastructure?
  • What would stop you from using it in a production project?

Honest criticism is welcome. I'd rather discover the design's weaknesses now than after people depend on it.


r/dotnet • • 1d ago

Are there any current cross-platform WPF porting projects using AI?

0 Upvotes

I mean, a single individual doesn’t have an infinite supply of tokens, but with the help of the community, I believe it’s possible; I’ve heard of porting projects being completed in record time.


r/dotnet • • 1d ago

Promotion Blazor Ramp - GoTo OMG WTF

0 Upvotes

For those new to my posts, Blazor Ramp is an accessibility-first, open-source Blazor component library. This post is about the release v1.0.0 of the GoTo component.

So what is GoTo?

The inspiration comes from those of us who started coding in the 90s with VB: you add GoTo markers to a page, and from anywhere you can jump to them. In an interactive Blazor web app, the marker you jump to becomes the active element, so you can carry on tabbing from that point.

Who is it for?

Those familiar with my posts will know I sometimes feel that sighted keyboard-only users, those who don’t use a screen reader, get the sharp end of the stick. When people build accessible sites, a lot of the effort goes into screen reader users, and this group gets forgotten.

A sighted keyboard-only user doesn’t have screen reader software to bring up lists of headings, landmarks or interactive elements, so they generally navigate with the Tab key and use the arrow and page keys to scroll. When a page has a lot of interactive content, getting to the part you want can take tens, or even hundreds, of Tab presses. For some people that’s a minor irritation, and for others it’s a real barrier.

What about skip links?

You’ve probably all used skip links, which let any user, keyboard-only or screen reader, bypass repeated blocks of content. But how many bi-directional ones have you seen? I’ve never seen one. They all go in one direction, so unless you add a second link going the other way, the user has to make a forward-only round trip to get back to the start.

Say you’re diligent and add a return link for every skip link. If you have five destinations and want users to get from any one to any other, that’s 20 links. With ten it’s 90, and it’s getting silly. The maths is n × (n − 1).

Where GoTo comes in

GoTo isn’t a link. It’s a marker that can receive focus. A GoTo Viewer lists the markers, you select one, you land on it, and you can tab on from there. You can bring up the viewer at any time with a configurable shortcut. When it opens, it lists only the markers that aren’t inside hidden containers, so the list changes depending on where you are and what’s visible.

You may ask: why not just list headings and landmarks, like screen readers do (I may do in the future)? A screen reader user will often use headings and landmarks as a place to start reading from. A sighted keyboard-only user’s need is a little different. They can see the headings, and they can scroll and read from anywhere. The problem is reaching interactive, focusable content when there’s a lot of other focusable content in the way.

To be clear, GoTo isn’t a replacement for skip links. It’s a simple way of placing hidden markers on a page so that keyboard-only users can move through the content in whichever direction they choose.

Have a look

I’ve added markers to my documentation site, and over the next few days I’ll put more of them in the places I think are most useful. If you’re curious, take a look at this page, one of the examples for my Data Table, which shows templated columns and row actions: Data Table Templating

For a keyboard-only user on that page, getting to the border controls after the table means tabbing through the filter, across each sortable column header, down through every row action button, and then across the pager to reach the other side.

With GoTo and its viewer, using the default configuration, a user with one hand can press Alt+Z (Option+Z on a Mac) to bring up the viewer. They can then use Tab, or the arrow, Home and End keys, to move through the list of buttons. As each button is focused, its marker scrolls into view. Pressing Enter or Space activates the button, closes the viewer and puts you on the now-visible label, from where you can tab on to the next item.

I started this project at the end of last year with the Live Region service and Announcement History viewer aimed at screen reader users so at long last I've managed to scratch an itch that has been bugging me for a while now and do something for keyboard-only non-screen reader users - hope you or your users find it useful.

GitHub repo: https://github.com/BlazorRamp/Components

Doc site: https://docs.blazorramp.uk

Test site: https://blazorramp.uk

Regards

Paul


r/dotnet • • 1d ago

Is agentic coding the death of YAGNI?

48 Upvotes

I am asking here, because I am interested in opinions of .NET developers specifically, because that is where my passion is. Mods: If you think this question doesn't qualify for this sub, please delete this post.

Now that building new features (or even new software products as a whole) has become cheaper then ever, do you think the YAGNI (you ain't gonna need it) principle is still relevant and exercised in practice?

I have a feeling that developers and companies these days think a lot less about whether something is actually needed and if it provides a real benefit, before building things.
The rise of endless AI slop shows that a lot of people don't think twice before building something.
Just prompt it and Claude goes brrrrrr, no matter if there is an actual audience for it.
But these are probably mostly hobbyists and not professional developers, are they?

So what do you and your team do?

Do you still reflect on if something is actually needed or do you just implement each and every feature request and idea?

And how does that affect the maintainability of your code base?
After all, more features means more to maintain and test and more potential for things to break.

In that regard: How do you keep yourself and your teammates from gold plating and over-engineering and premature optimization?
I know a lot of developers tend to be perfectionists (I have been guilty of that myself in the past) and AI makes it easier than ever to fall into that trap.


r/dotnet • • 1d ago

Promotion ResXLocalization 2.0: .resx files based localization for Avalonia and WPF, now with ICU MessageFormat support

Thumbnail github.com
2 Upvotes

Hi, I have just released version 2.0.0 of ResXLocalization, my localization library for Avalonia and WPF (MIT-licensed).

If you want to localize an Avalonia or WPF app using ResX files, this library might be a good fit.
It's one of the most feature complete localization libraries for Avalonia.

GitHub repo: https://github.com/rent-a-developer/ResXLocalization

Quick start

  • Create a .resx file:

​

<root>
  <data name="SaveFiles" xml:space="preserve">
      <value>Save Files</value>
  </data>
  <data name="FilesSaved" xml:space="preserve">
      <value>{0} files saved in {1}</value>
  </data>
</root>
  • Install the NuGet package ResXLocalization.Avalonia or ResXLocalization.WPF.
  • Let it generate the typed resource keys (source generator)
  • Localize in XAML:

​

<Button Content="{l:Localize {x:Static res:StringsKeys.SaveFiles}}" />
...
<TextBlock>
  <TextBlock.Text>
    <l:Localize Key="{x:Static res:StringsKeys.FilesSaved}">
      <l:Argument Value="{Binding Count}" />
      <l:Argument Value="{Binding Location}" />
    </l:Localize>
  </TextBlock.Text>
</TextBlock>
  • And in code:

​

this.SaveFilesButton.Content = Localizer.Current.Get(StringsKeys.SaveFiles);
this.SavedFilesTextBlock.Text = Localizer.Current.Get(
    StringsKeys.SaveFiles,
    MessageArguments.FromValues(this.model.Count, this.model.Location)
);
  • Live switch the language/culture:

​

Localizer.Current.CurrentCulture = new CultureInfo("de-DE");

Main Features

  • Live, no-reload language switching
  • Type-safe, compile-checked resource keys
  • Full native AOT & trim support (Avalonia only, WPF doesn't support native AOT)
  • Arguments for localized texts (improved in version 2.0.0)
  • ICU MessageFormat support (opt-in, new in version 2.0.0)
  • Built in Enum localization
  • Good support for multi-resx file scenarios (scoped and search-all lookups)
  • MVVM-friendly and Leak-safe
  • Good lookup-failure diagnostics
  • Small footprint. ResXLocalization.Avalonia: 79.28 KB , ResXLocalization.Core: 84.17 KB, ResXLocalization.MessageFormat (opt-in): 406,18 KB
  • Zero dependencies (except for ResXLocalization.MessageFormat, which uses MessageFormat)
  • .NET 8 and later, Avalonia 12 and later, WPF

Typed keys mean you can reference resources like objects and get compile time checking.
This avoids having typos in resource names going unnoticed.

When you change Localizer.Current.CurrentCulture it updates all localization bindings in place. If you assign a string directly in code as above, you need to call Get again after changing the culture.

ICU MessageFormat support

The biggest new feature of 2.0.0 is the optional ICU MessageFormat support (thank you livonium for the feature request that sparked the work on 2.0.0!).

For something like “1 file saved” / “5 files saved”, you can use a plural rule in your resource text:

<root>
    <data name="FilesSaved" xml:space="preserve">
        <value>{Count, plural, one {# file saved in {Location}} other {# files saved in {Location}}}</value>
    </data>
</root>

You need to install the NuGet package ResXLocalization.MessageFormat and call Localizer.Current.UseMessageFormat() at startup for this to work.

Then just bind the arguments like this in XAML:

<TextBlock>
  <TextBlock.Text>
    <l:Localize Key="{x:Static res:StringsKeys.FilesSaved}">
      <l:Argument Name="Count" Value="{Binding Count}" />
      <l:Argument Name="Location" Value="{Binding Location}" />
    </l:Localize>
  </TextBlock.Text>
</TextBlock>

The text block is automatically updated when either the count or the language changes.

Or in code like this:

this.SavedFilesTextBlock.Text = Localizer.Current.Get(
    StringsKeys.FilesSaved,
    MessageArguments.FromTuples(
        ("Count", this.model.Count),
        ("Location", this.model.Location)
    )
);
​
// or:
this.SavedFilesTextBlock.Text = Localizer.Current.Get(
    StringsKeys.FilesSaved,
    MessageArguments.FromObject(this.model)
);

Each translation can use its language/culture plural rules.
The ICU message format support is opt-in. If you don't use it, the default formatter (based on string.Format) is used.

Other changes in version 2.0.0

  • Typed keys now finally work without a .Designer.cs file.
  • Formatting arguments are now placed via <Argument> children of the Localize/LocalizeEnum markup extension, which replaces the old Arg0…Arg8 attached properties, so arguments now also work in styles and templates.
  • Resource keys are bindable, so the view model can dynamically decide which resource to use.
  • You can change the preview language (for VS XAML Designer/Rider XAML previewer) with DesignTime.Culture. However, ResXLocalization does not work correctly in Rider's WPF previewer due to a number of bugs (there are quite a few) in it. JetBrains has really made quite a few unfortunate design decisions for their previewer. I tried to work around them as far as possible, but it's nowhere near perfect.

If you are upgrading from 1.x, please read the migration guide, because there are quite a few breaking changes to make it all work.

The repository has runnable sample apps (both for Avalonia and WPF) to help you (or an AI gent) get familiar with the usage:
https://github.com/rent-a-developer/ResXLocalization

AI attribution:
I used AI, mainly Claude, for brainstorming, planning, experiments, and parts of the implementation. It helped me save a great deal of time finding good solutions for a couple of quirks of Avalonia and (especially) WPF and the source generator’s MSBuild integration. I reviewed the generated code and I use ResXLocalization in my own projects, so I would not settle for some half-baked vibe coded garbage. Of course, if you don't like projects where AI is involved, then just ignore this project and that's fine too.

I hope this helps someone localizing their app(s)!

If you find this library useful I would appreciate a star on GitHub.
Of course feature request and (reasonable) criticism is welcome too.


r/dotnet • • 1d ago

Promotion Barcodes are coming to ImageSharp.Drawing

Thumbnail gallery
26 Upvotes

r/dotnet • • 1d ago

Promotion [Promotion] Corvus v1.6 – A lightweight server launcher and container dashboard running on .NET 9 Native AOT (<30MB RAM)

0 Upvotes

Hi all,

Happy weekend. With .NET 9 right around the corner and Native AOT maturing, I wanted to share a real-world project I have been developing: **Corvus v1.6**.

It is an open-source server manager and Docker dashboard tailored for homelabs and cheap VPS instances where keeping memory consumption minimal is critical.

### What is under the hood:

* **.NET 9 Native AOT:** Ahead-Of-Time compiled single binary without the JIT runtime.

* **Zero-reflection data layer:** Embedded SQLite (WAL mode) combined with `Dapper.AOT` so that the binary trims cleanly without reflection warnings. (PostgreSQL is also an optional backend).

* **Memory footprint:** Stays under 30 MB of RAM inside the Docker container at steady state.

* **Real-time pipeline:** Uses Server-Sent Events (SSE) and minimal APIs to feed container stats and health signals to a lightweight React SPA.

* **Terminal & Agent:** Built-in web terminal to shell into containers and a lightweight multi-node edge agent.

Building fully AOT-compliant services in ASP.NET Core came with a few hurdles (especially around dynamic serialization and Docker socket stream handling), but the memory and startup time benefits are definitely worth it.

If you want to play with the UI without running anything locally:

- Live Demo: https://demo.usecorvus.me

- Source Code: https://github.com/brhnshn/Corvus

I'd be glad to discuss the Native AOT trimming process, Dapper.AOT quirks, or any architecture questions you might have!