r/dotnet • • 1h 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

• 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 • • 30m ago

Parsley: An extremely capable parser generator for C#

• 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 • • 10h ago

Is agentic coding the death of YAGNI?

36 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 • • 7h ago

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

Thumbnail github.com
12 Upvotes

r/dotnet • • 2h 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 • • 8h ago

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

3 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 • • 13h 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

.NET developers who moved into mobile - what did you find challenging?

37 Upvotes

Hey everyone,

I'm curious whether other .NET/C# developers have had a similar experience.

I've been a .NET/C# developer for about 15 years, mostly building cross-platform mobile apps - first with Xamarin and now with .NET MAUI. I've noticed that for developers whose experience is mainly in desktop, web or backend development, transitioning into mobile development can be challenging.

If you’ve made that transition, what did you find most challenging? Were there things you had to learn that weren't obvious from your existing .NET experience?

I'm asking because I'm considering creating an online course for .NET/C# developers making that transition. I'm trying to understand whether there's a real need for it. What would you expect a course like that to cover? Or did existing docs, tutorials, or other resources already give you what you needed?

Genuinely interested in your answers!

Cheers,
Andi


r/dotnet • • 7h 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 • • 16h ago

Promotion Barcodes are coming to ImageSharp.Drawing

Thumbnail gallery
2 Upvotes

r/dotnet • • 1d ago

Promotion Dapr 1.19 is here, with faster workflows and new data APIs!

15 Upvotes

Dapr, the Distributed Application Runtime, a graduated OSS project within the CNCF with over 700k weekly NuGet downloads, has released a new version. What it means for you:

  • Faster workflows, jobs, and reminders: in nightly performance tests against 1.18.0, workflow throughput is up to 40% higher, actor reminder registration p99 latency is 71% lower, and jobs scheduling p99 latency is 33% lower
  • Leaner workflow workers: the .NET SDK now get only the new events of each turn, not the full history
  • Easier workflow operations: list, inspect, and rerun workflows from the .NET SDK
  • Workflow governance: operate on workflows owned by other apps, with WorkflowAccessPolicy deciding which operations each caller may perform, and requires rules that allow an operation only when the caller's history contains the events you list
  • More for AI agents: new alpha APIs to store files, search documents, and query vectors, plus a Dapr MCP server
  • A simpler control plane: run actor placement from the Scheduler and drop one service

Read the announcement: https://blog.dapr.io/posts/2026/10/22/dapr-v1.19-is-now-available/


r/dotnet • • 22h ago

Article Building a local LLM inference engine in C#/.NET: multi-GPU scheduling, native CUDA, and benchmark results

Thumbnail github.com
4 Upvotes

I’m the developer of TensorSharp, an open-source .NET 10 inference engine for running local GGUF models. It can be embedded in .NET applications or run through a CLI and an OpenAI/Ollama-compatible server.

The project combines managed C# with native accelerator backends. The benchmarks below use native CUDA/GGML execution, including TensorSharp-specific kernels. The performance improvements come from changes to scheduling, data movement, and model execution.

I recently worked on DeepSeek V4.1 Flash 1017 on five NVIDIA A40s, and thought the engineering details might be interesting to people building performance-sensitive .NET applications.

Making the GPUs work concurrently

One of the biggest problems was in the native tensor-parallel execution path.

The model’s routed experts were distributed across GPUs, but execution repeatedly passed through a synchronous, host-driven path. Each layer involved multiple host round trips and synchronization points.

The revised implementation:

  • Schedules work concurrently across GPU ranks.
  • Uses CUDA events to order dependent operations.
  • Overlaps the shared expert’s computation with the routed experts.
  • Removes host waits from inside the layer’s execution path.

Cross-GPU transfers still use pinned host-memory staging because peer access was not working correctly on this machine.

In the implementation comparison, this change took tensor-parallel decode from 15.2 to 36.3 tokens/sec after a 2,048-token prompt with full prefill enabled.

For me, this was a useful reminder that distributing work across devices is only the beginning; the synchronization and data-transfer design determine how much concurrency you actually get.

Making native failures recoverable

The reliability work was just as interesting.

Previously, an exception on a native worker could terminate the server. A failure on the coordinating rank could leave the other workers waiting indefinitely at a barrier.

The fixes coordinate error reporting, ensure every rank reaches the required synchronization points, and drain work already issued by a failed layer. Failure-injection tests now check both the failure path and recovery on the next forward pass.

When native workers run inside a .NET application, that lifecycle becomes part of the application’s reliability story.

A small benchmark snapshot

The comparison used the same Unsloth UD-IQ2_XXS GGUF files on the same five-A40 machine, with the Engram lookup table in host memory.

TensorSharp used --tp 5. The llama.cpp baseline was Unsloth’s dsv41-all fork at be836572, using layer splitting; its row/tensor split modes did not successfully run this model in the tested build.

These are real-text server results for approximately 1,800-token prompts, generating 256 tokens with greedy decoding and thinking disabled. Each configuration had a warm-up and three measured requests, with unique prefixes to prevent prefix-cache reuse.

Measurement TensorSharp llama.cpp Throughput ratio
Prompt processing, YOCO disabled 563.9 tok/s 174.6 tok/s 3.23×
Prompt processing, default YOCO enabled 960.5 tok/s 174.6 tok/s 5.50×
Token generation, no drafter 35.5 tok/s 29.6 tok/s 1.20×
Token generation with DSpark speculative decoding 55.0 tok/s 41.3 tok/s 1.33×

Prompt processing is also called prefill. The first three rows report medians; the DSpark row reports means, with the drafter enabled in both engines. TensorSharp’s generation rows use its default YOCO configuration.

The speed/quality trade-off

YOCO bounded replay reduces prompt-processing work by running selected later layers over only the final 140 positions of eligible text-prefill calls. It is approximate and can be disabled with:

TS_DSV41_YOCO_KEEP=0

The 3.23× prefill result above has this approximation disabled.

On the report’s WikiText-2 evaluation, perplexity—lower is better—was:

  • llama.cpp: 2.4697
  • TensorSharp, YOCO disabled: 2.4811
  • TensorSharp, YOCO enabled: 2.4978

Enabling YOCO increased perplexity by approximately 0.67% relative to TensorSharp’s full-prefill result. With YOCO enabled, perplexity was approximately 1.14% above llama.cpp’s.

That is a measured trade-off on a limited text evaluation, rather than a general percentage change in model capability. Disabling YOCO also does not make the two engines numerically identical.

Scope of the results

These measurements cover one model quantization and one hardware configuration. The llama.cpp server results came from an earlier session on the same machine and showed some unexplained prefill variability.

At measurement time, the TensorSharp build included uncommitted changes, and the raw validation records were also uncommitted. The report records the build details and remaining limitations; the current repository should not be assumed to reproduce every number directly.

The five-GPU setup is specific to this benchmark. TensorSharp also supports smaller models and managed CPU, native CPU, CUDA, Metal, and Vulkan execution paths, with support varying by model.

Links:

If you’re integrating local models into .NET applications, do you prefer an in-process engine or a separate inference server? I’d be interested in what drives that choice in practice, especially around deployment, memory ownership, and recovering from native failures.


r/dotnet • • 9h 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

Article I built a .NET LLM inference engine that runs an 816B MoE model across 10 GPUs — here’s what I learned about C# and native performance

Thumbnail github.com
157 Upvotes

I’ve been developing TensorSharp, an open-source LLM inference engine and agent runtime built with C#/.NET.

One question I’ve been exploring is:

Can .NET serve as the foundation for a high-performance LLM inference engine, rather than just being a wrapper around Python-based inference servers?

Recently, I got Muse Spark 816B (42B active parameters) running across 10 NVIDIA A40 GPUs and benchmarked TensorSharp against llama.cpp.

I wanted to share some results and engineering lessons, particularly around managed/native execution, multi-GPU scheduling, and where C# fits into performance-critical AI infrastructure.

The architecture

TensorSharp uses C#/.NET for the inference engine’s orchestration, model execution management, request scheduling, KV cache management, and agent runtime.

GPU computation is handled through native backends, including CUDA, Metal, and Vulkan.

The idea isn’t to reimplement CUDA kernels in C#. Instead, it’s to use .NET for the higher-level systems engineering while keeping computationally intensive operations in optimized native code.

This separation has some interesting benefits:

  • A unified runtime for model inference and agent execution.
  • Cross-platform support through different GPU backends.
  • Integration with existing .NET applications without requiring a separate Python inference service.
  • The ability to implement scheduling, caching, concurrency, and agent workflows in C#.

But it also raises an important performance question: where should the boundary between managed and native execution be?

The experiment: 816B parameters on 10 GPUs

Test configuration:

  • Model: Muse-Spark-Open-816B-A42B
  • Quantization: Unsloth UD-IQ3_XXS (~316 GiB of weights)
  • Hardware: 10× NVIDIA A40 (46 GB each)
  • Context: 32K tokens
  • KV cache: F16
  • All model experts resident in GPU memory

I tested two multi-GPU execution strategies:

Layer splitting: Different GPUs own different layers.

Tensor parallelism: Each layer is distributed across multiple GPUs, requiring synchronization and communication.

Both were compared against llama.cpp on the same machine using the same model and requests.

Results

Benchmark TensorSharp llama.cpp
Layer split: single-request decode 16.69 tok/s 16.79 tok/s
Layer split: 4K prefill 999.7 tok/s 483.2 tok/s
Layer split: 16K prefill 1,346.8 tok/s 826.1 tok/s
Tensor parallel: decode 22.63 tok/s 16.19 tok/s
Tensor parallel: 4 concurrent requests 45.73 tok/s 24.49 tok/s

The most interesting result to me isn’t necessarily the comparison against llama.cpp.

It’s how much performance changed after moving the right parts of execution across the managed/native boundary.

Lesson 1: Native execution granularity matters enormously

TensorSharp previously had a managed tensor-parallel execution path that dispatched operations individually.

It worked, but performance was poor.

On this model, it achieved only 1.9–2.4 tokens/sec.

After switching to a native whole-model execution path using upstream ggml’s meta backend, TensorSharp reached 22.6 tokens/sec.

That’s roughly a 10× improvement over our previous implementation.

The lesson wasn’t that C# is inherently slow.

It was that executing too many fine-grained GPU operations across a managed/native boundary introduces unnecessary overhead.

Keeping the higher-level scheduling and orchestration in .NET while moving the performance-critical execution graph into native code made a huge difference.

Lesson 2: GPU scheduling can matter more than raw compute

For layer-split inference, we implemented pipelined prefill.

Instead of processing the entire prompt sequentially through GPU 0, GPU 1, GPU 2, etc., the prompt is divided into micro-batches.

While GPU 1 processes micro-batch A, GPU 0 can already start processing micro-batch B.

We also implemented adaptive micro-batch sizing and larger prefill steps for single-request workloads.

With these changes, TensorSharp reached approximately 1,347 prefill tokens/sec on a 16K-token prompt.

That’s 1.63× the llama.cpp result on the same hardware.

This is a good example of why inference performance isn’t just about writing faster kernels. The execution and scheduling strategy matters enormously.

Lesson 3: More parallelism isn’t always faster

Tensor parallelism improved generation throughput, but it was significantly slower than layer splitting for long-prompt prefill.

In TensorSharp:

  • Layer-split prefill: ~1,347 tok/s
  • Tensor-parallel prefill: ~295 tok/s
  • Layer-split decode: ~16.7 tok/s
  • Tensor-parallel decode: ~22.6 tok/s

The reason is communication overhead.

Muse Spark’s MoE architecture requires substantial cross-GPU reductions, and this particular machine had broken GPU peer-to-peer transfers.

NCCL therefore had to communicate through host memory.

This made the optimal execution strategy workload-dependent.

It’s also an interesting problem for a .NET scheduler: selecting execution strategies based on model architecture, hardware topology, and request characteristics.

Lesson 4: Performance optimizations need correctness validation

We also compared next-token probability distributions against llama.cpp on 12,276 positions from WikiText-2.

TensorSharp achieved:

  • 98.84% top-1 agreement in layer-split mode.
  • 98.82% top-1 agreement in tensor-parallel mode.
  • Perplexity of approximately 1.848, compared with llama.cpp’s 1.847.

We additionally ran integration checks covering JSON generation, tool calling, multi-turn conversations, cache reuse, and concurrent requests.

TensorSharp passed 13/14 checks with layer splitting and 14/14 with tensor parallelism.

These aren’t comprehensive model-quality evaluations, but they help catch regressions while optimizing the execution engine.

A note about the comparison

There are some important limitations.

llama.cpp’s NCCL-enabled build failed on this particular 10-GPU setup due to CUDA peer-access restrictions, so its tensor-parallel results used the non-NCCL implementation.

TensorSharp includes a workaround that allowed NCCL to run over shared memory.

Therefore, the tensor-parallel performance gap should not be interpreted as a general advantage over llama.cpp on all hardware.

The layer-split comparison is more directly comparable, and single-request decode performance was essentially identical.

All measurements were taken on one machine with one model. I’d like to test more hardware configurations, especially systems with working P2P or NVLink.

Why I’m sharing this here

A lot of LLM infrastructure is built around Python, C++, and CUDA.

I think there’s an interesting opportunity for .NET to participate more directly in this space, especially for developers who want to build AI-powered applications without maintaining a separate Python-based inference stack.

TensorSharp is my attempt to explore that direction.

It’s open source, and the full benchmark report includes the methodology, configuration, limitations, and reproduction scripts.

GitHub: https://github.com/zhongkaifu/TensorSharp

Full benchmark: https://github.com/zhongkaifu/TensorSharp/blob/main/docs/perf/muse-spark-vs-llama-cpp.md

I’m particularly interested in hearing from other .NET developers:

Have you worked on performance-critical systems that combine C# with native C++/CUDA libraries? Where have you found the managed/native boundary works best?

And more broadly, do you think .NET has a larger role to play in local AI inference infrastructure, or is it better suited to application-level orchestration?


r/dotnet • • 9h 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

Do you stick strictly to LTS releases in production, or do you deploy STS (Standard Term Support) releases as well?

18 Upvotes

Do you stick only to LTS .NET versions in production?

Running a poll on this currently. I knew many .NET devs liked to stick to LTS versions only, but wasn't expecting it to be so high, although this is likely to not be representative, I'm not sure.

Microsoft have confirmed there is no difference in quality between STS and LTS releases, and not too long ago STS releases moved from 18 to 24 months support.

So why in your opinion is there still so strong preference for STS? Is some of this coming from other platforms where STS releases might indeed be 'experimental'?

I didn't want to create a poll here too to preserve the numbers so please consider voting in the poll if you like and can, thank you -> https://www.linkedin.com/posts/davidcallan_net-developers-do-you-stick-strictly-to-activity-7517560007018823681-ZHsd


r/dotnet • • 16h 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!


r/dotnet • • 1d ago

List<T> in C# vs SQL

Thumbnail
0 Upvotes

r/dotnet • • 21h ago

Article Benchmarked Temporal’s .NET SDK around LLM calls: 24–28 ms at p95 outside the model

Thumbnail threadunsafe.dev
0 Upvotes

r/dotnet • • 18h ago

Promotion I built Magpie, a self-hosted media discovery and automation app in C#

Post image
0 Upvotes

I built Magpie over the last year or so to solve a lot of my frustrations with the *arr suite of apps.

  • Smarter acquisition, better lifecycle management (no more stuck downloads/manual intervention)
  • Real user accounts and RBAC
  • One app that can manage just about any content type via extensions
  • Multinode cluster support
  • Many others that I can't think of at the moment

Most functionality is plugin-based, including content types, so it can handle movies, TV, music, books, and whatever else anyone wants to build.

I’ve been running it as my full *arr replacement for months and shaking out bugs along the way, and I think it’s in a really good state.

Plugins also cover media server, download clients, and indexer integrations, as well as metadata discovery. They can be searched and installed directly from the UI. I have several plugins built already and will be adding lots more.

It also supports several deployment models and database backends.

I’d love for people to check it out and let me know what they think.

GitHub

PS, to share a few cool implementation tidbits (to me anyway):

  • Plugins are DI-native, so you can override built-in services, add API endpoints, and even package frontend extensions as web components from your .NET code.
  • There is a custom MLFQ-style (multilevel feedback queue) job scheduler so long-running background jobs don’t hog resources (although you can change how it works with plugins, like most things).
  • It has a custom query language for library queries and external metadata providers.

r/dotnet • • 1d ago

I got tired of Windows Explorer freezing on 50,000 small files, so I built RoboCopyUI — an open-source, multi-threaded GUI in .NET 10 / WPF (Hit 2.0 GB/s on NVMe). Would love your architectural feedback

Thumbnail reddit.com
0 Upvotes

r/dotnet • • 1d ago

[Open Source] Created a simple tool to fix broken Windows Update policies and reliably install .NET Framework 3.5 (dev on .NET 4.7.2)

Thumbnail
0 Upvotes

r/dotnet • • 1d ago

Has Business Logic in UI Development Already Moved Beyond Human Hands, Except for Core UI Work?

0 Upvotes

I’ve been using AI extensively for UI development for some time now.

My recent projects have included:

  • React SPA + MUI (with MCP)
  • Avalonia Windows applications

Based on what I’ve experienced while developing these applications, I’m starting to feel that AI may be able to handle almost every aspect of UI development.

On our side, we mainly use Kiota to expose the server API specifications and contracts.

Even with relatively simple prompts, AI can inspect those API definitions and understand the available endpoints, request and response models, and related structures. From there, it can build a usable UI from scratch.

We also use AGENTS.md and PLAN.md to define architectural guidelines and conventions for areas such as the service layer and ViewModel layer, and have AI work within those rules.

So far, it has been able to handle the vast majority of the work.

We also use AI for server-side development, but I see a significant difference there.

Backend frameworks have much larger specifications, architectural constraints, and more complex business requirements. Because of this, we still rely heavily on human-written and human-reviewed reference implementations, and then have AI generate code based on those established patterns.

Even with this approach, I still see problems occasionally—for example, AI generating queries with O(N)-style access patterns or introducing other performance issues.

UI development, however, feels fundamentally different.

The improvement in productivity has been remarkable.

What surprised me even more was seeing AI directly launch a native application, interact with it, test it, identify problems, modify the code, and test it again.

Watching that entire process was both shocking and, frankly, almost surreal.

It made me wonder:

Will junior frontend developers eventually become unnecessary?

I’m starting to worry that, in the long run, only developers who can design and maintain the core UI architecture, frameworks, components, and development standards will remain essential.

The implementation of ordinary business screens may increasingly become something AI can handle almost entirely on its own.

What do you think?


r/dotnet • • 1d ago

Implement Unit tests with sitecore CMS 10.4 XM/XP in project

2 Upvotes

I work with sitecore CMS 10.4 XM/XP and this is the first time we are trying to implement unit testing by writing testcases for the sitecore project that I am working with. We are planning to think of writing a use case for Google bots which crawl into our website and trying to automate a test that could allow the bots only in production to crawl. If anyone, who already implememted this usecase or maybe provide an idea or enlighten on this. All the responses are much apreciated


r/dotnet • • 2d ago

The death of Open Source in the NuGet eco-system

187 Upvotes

One by one, most of the major open source packages in NuGet are going the paid commercial license route. Its becoming harder and harder to write .NET software in a work setting as most of the crucial NuGet packages we used to depend on are becoming paid software. With the proliferation as part of our daily driver tool kit, justifying commercial license for NuGet packages to stakeholders is harder than it's ever been as they often think AI can write a free alternative (which, I mean, they're not completely wrong). I'm absolutely frustrated by the continuous bait and switch licensing that's been happening in the eco-system as well. Some developers have done a much better job than others in communicating and preparing for licensing changes, however some have absolutely trojan horsed us into some pretty difficult positions. I also resent the fact that .NET and NuGet don't give you better tools to manage, control, and protect yourself from installing packages with paid or specific licensing requirements.