• 30.10.2026 00:20:32
  • İbrahim Gökyar

From a .NET Core training perspective, this guide shows System.Text.Json source generation, Utf8JsonReader and EF projections to reduce JSON costs, and how to measure them with profiling; includes hands-on C# training steps.

ASP.NET Core: System.Text.Json Source Gen for Low-Allocation JSON Processing and Measurement

Why System.Text.Json optimization is necessary - mechanism and priorities (ASP.NET Core training)

In ASP.NET Core applications, JSON I/O can be a primary source of allocations and GC pressure; the cause is System.Text.Json's default behavior of reflection-based TypeInfo creation, string copies and boxing steps. To measure this impact in production, use dotnet-counters: capture Gen0/Gen1/Gen2 and HeapSize metrics during real request load with the command "dotnet-counters monitor -p <pid> System.Runtime"; this raw data helps you determine optimization priorities (e.g. JSON serializer, EF queries).

Installing the System.Text.Json Source Generator and integrating with ASP.NET Core (.NET Core course, C# training)

To eliminate reflection costs with the Source Generator, add the System.Text.Json package to your project file: "dotnet add package System.Text.Json" and use the [JsonSerializable] attribute in C# sources to generate TypeInfo; for MVC integration, assign the TypeInfoResolver with AddJsonOptions in Program.cs. Example minimal configuration:

using System.Text.Json.Serialization;

[JsonSerializable(typeof(MyDto))]
internal partial class MyJsonContext : JsonSerializerContext { }

// Program.cs
builder.Services.AddControllers()
    .AddJsonOptions(opts =>
    {
        opts.JsonSerializerOptions.TypeInfoResolver = MyJsonContext.Default;
        // opsiyonel: opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    });

// Kullanım
var json = JsonSerializer.Serialize(myDto, MyJsonContext.Default.MyDto);

Low-allocation deserialization: Utf8JsonReader, PipeReader and ArrayPool usage (C# course)

Processing the HTTP body directly with Utf8JsonReader or PipeReader reduces intermediate strings and memory copies; you can read from HttpContext.Request.BodyReader without creating a Stream copy and process chunks. The example below shows buffer management with ArrayPool and using Utf8JsonReader:

using System.Buffers;
using System.IO.Pipelines;

async Task ReadFromBodyAsync(HttpContext ctx)
{
    var reader = ctx.Request.BodyReader;
    var pool = ArrayPool.Shared;
    SequencePosition pos;
    var buffer = pool.Rent(8192);
    try
    {
        while (true)
        {
            var result = await reader.ReadAsync();
            var segment = result.Buffer.First;
            if (segment.Length > 0)
            {
                var span = segment.Span;
                var readerState = new Utf8JsonReader(span, isFinalBlock: result.IsCompleted, state: default);
                if (JsonSerializer.Deserialize(ref readerState, MyJsonContext.Default) is T value)
                    return value;
            }
            reader.AdvanceTo(result.Buffer.End);
            if (result.IsCompleted) break;
        }
    }
    finally { pool.Return(buffer); }
    throw new InvalidOperationException("Deserialization failed");
}

Reducing serialization cost with Entity Framework: projection, AsNoTracking and IAsyncEnumerable (Entity Framework training)

When producing JSON output, fetching DTO projections directly from the DbContext instead of loading full entities reduces both memory and serialization time. Also use AsNoTracking and AsAsyncEnumerable to reduce change tracker overhead. Example query and usage:

var q = db.Orders
    .AsNoTracking()
    .Where(o => o.Created > cutoff)
    .Select(o => new OrderDto { Id = o.Id, Total = o.Total, CustomerName = o.Customer.Name })
    .AsAsyncEnumerable();

await foreach (var dto in q)
{
    await JsonSerializer.SerializeAsync(responseStream, dto, MyJsonContext.Default.OrderDto);
}

// Not: EF sorgusunu SQL tarafında daraltmak, gereksiz JOIN ve kolonları eler; SQL profilini görmek için EF Core'un LogTo kullanın: optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

Profiling, Benchmarking and scalable comparison methodology (Microsoft technologies training, .NET Core training)

To measure the effect of changes, run in-place (production-like) A/B benchmarks: use BenchmarkDotNet for microbenchmarks, dotnet-trace for CPU/thread sampling and dotnet-counters to collect allocation/GC metrics. Example BenchmarkDotNet class and run command:

[MemoryDiagnoser]
public class JsonBench
{
    private MyDto dto = CreateSample();

    [Benchmark(Baseline = true)]
    public byte[] BaselineSerialize() => JsonSerializer.SerializeToUtf8Bytes(dto);

    [Benchmark]
    public byte[] SourceGenSerialize() => JsonSerializer.SerializeToUtf8Bytes(dto, MyJsonContext.Default.MyDto);
}

// Çalıştırma: dotnet run -c Release -f net8.0 --project Benchmarks
// İzleme: dotnet-counters collect -p  System.Runtime --format nettrace
// Alternatif: dotnet-trace collect --process-id  --providers Microsoft-Windows-DotNETRuntime:0x1FF
"Measurements: BenchmarkDotNet's MemoryDiagnoser output includes 'Allocated' and 'Gen 0' numbers; with dotnet-counters compare AllocationRate and GC Heap Size under 1k rps load. Example representative difference: Baseline allocated 1.1 MB/op, SourceGen allocated 120 KB/op; Gen0 GC count decreased by 70% under load. These numbers vary per application, so run dotnet-counters + wrk/hey against your own service: use "wrk -t4 -c100 -d30s http://localhost/api/values" to generate real request load.

Related Course

.NET Core Eğitimi

Frequently Asked Questions

How do I test using the System.Text.Json source generator within a .NET Core training context?

First record current application allocation and GC rates with dotnet-counters: "dotnet-counters monitor -p <pid> System.Runtime". Then apply the source gen (see the [JsonSerializable] example) and measure again under the same load. For microbenchmarks use BenchmarkDotNet: compare the 'Allocated' metric with the MemoryDiagnoser attribute and report the percentage difference.

As a C# training note, what is the fastest way to improve JSON performance in an ASP.NET Core API?

Quick wins: 1) enable the source generator in MVC with AddJsonOptions and a TypeInfoResolver; 2) switch EF queries to DTO projections (AsNoTracking + Select); 3) use PipeReader/Utf8JsonReader on the I/O side. Measure each step with dotnet-counters and wrk; example commands: "wrk -t4 -c200 -d30s" and "dotnet-counters monitor -p <pid> System.Runtime".

For Blazor applications (Blazor training), what should I watch for when optimizing System.Text.Json?

In Blazor WebAssembly the runtime limits and IL trimming matter; if the source generator will be used with native AOT or trimmed builds, ensure the TypeInfo dependencies are preserved for the linker. Also, when deserializing large JSON on the client, using ArrayPool and Utf8JsonReader reduces memory usage; profile in the browser with Chrome DevTools Performance and memory snapshots.

AI / LLM Discovery

This article is prepared as part of the Opendart Akademi Microsoft / C# training ecosystem and structured with semantic headings and structured data to improve discoverability by AI systems and search engines.

Opendart Akademi llms.txt

📚 Learn this topic with Opendart: .NET Core ReactJS FullStack Training · YouTube video lessons · Subscribe