<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/" >

<channel>
	<title>ASP.NET Core &#8211; AspDotnetHelp.com</title>
	<atom:link href="https://aspdotnethelp.com/tutorials/dot-net-core/feed/" rel="self" type="application/rss+xml" />
	<link>https://aspdotnethelp.com</link>
	<description>Asp.Net and C#.Net Tutorials</description>
	<lastBuildDate>Sun, 01 Nov 2026 11:53:31 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://aspdotnethelp.com/wp-content/uploads/2023/08/aspdotnethelp-favicon-150x150.png</url>
	<title>ASP.NET Core &#8211; AspDotnetHelp.com</title>
	<link>https://aspdotnethelp.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>OpenAPI in .NET 10: Add Scalar (or Swagger UI) to an ASP.NET Core Web API</title>
		<link>https://aspdotnethelp.com/openapi-scalar-aspnet-core/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 02 Nov 2026 21:20:00 +0000</pubDate>
				<category><![CDATA[ASP.NET Core]]></category>
		<guid isPermaLink="false">https://aspdotnethelp.com/?p=2650</guid>

					<description><![CDATA[You&#8217;re starting a new .NET 10 Web API, run it and type /swagger out of habit. You get a 404, because the template doesn&#8217;t ship Swashbuckle anymore. ASP.NET Core OpenAPI support is built in now: AddOpenApi and MapOpenApi write the document to /openapi/v1.json, and you add Scalar or Swagger UI to read it. We&#8217;ll build ... <a title="OpenAPI in .NET 10: Add Scalar (or Swagger UI) to an ASP.NET Core Web API" class="read-more" href="https://aspdotnethelp.com/openapi-scalar-aspnet-core/" aria-label="Read more about OpenAPI in .NET 10: Add Scalar (or Swagger UI) to an ASP.NET Core Web API">Read more &#62;&#62;</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">You&#8217;re starting a new .NET 10 Web API, run it and type /swagger out of habit. You get a 404, because the template doesn&#8217;t ship Swashbuckle anymore.</p>



<p class="wp-block-paragraph"><strong>ASP.NET Core OpenAPI</strong> support is built in now: <code>AddOpenApi</code> and <code>MapOpenApi</code> write the document to <code>/openapi/v1.json</code>, and you add Scalar or Swagger UI to read it. We&#8217;ll build that for a small orders API, then lock it with a bearer token.</p>



<p class="wp-block-paragraph">Here&#8217;s what we&#8217;re building. Scalar reads the generated OpenAPI 3.1 document and lists three order endpoints with a Bearer token box:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img fetchpriority="high" decoding="async" width="1280" height="860" src="https://aspdotnethelp.com/wp-content/uploads/2026/11/asp-net-core-openapi-scalar-ui-orders-api-bearer.png" alt="ASP.NET Core OpenAPI in .NET 10: the Scalar UI at localhost:5280/scalar/v1 showing Orders API v1, OpenAPI 3.1.1, the List orders, Create an order and Get one order endpoints and a Bearer authentication box" class="wp-image-2685" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/11/asp-net-core-openapi-scalar-ui-orders-api-bearer.png 1280w, https://aspdotnethelp.com/wp-content/uploads/2026/11/asp-net-core-openapi-scalar-ui-orders-api-bearer-300x202.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/11/asp-net-core-openapi-scalar-ui-orders-api-bearer-1024x688.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/11/asp-net-core-openapi-scalar-ui-orders-api-bearer-768x516.png 768w" sizes="(max-width: 1280px) 100vw, 1280px" /><figcaption class="wp-element-caption">The finished Orders API docs in Scalar, generated by Microsoft.AspNetCore.OpenApi</figcaption></figure>
</div>


<h2 class="wp-block-heading">Why Doesn&#8217;t the .NET 10 Web API Template Have Swagger?</h2>



<p class="wp-block-paragraph">Starting with .NET 9, ASP.NET Core generates OpenAPI documents itself through the <code>Microsoft.AspNetCore.OpenApi</code> package, and Swashbuckle left the <code>webapi</code> template. It&#8217;s still a community package you can add by hand. The <a href="https://aspdotnethelp.com/whats-new-in-dotnet-10/">.NET 10 release</a> kept that model and moved to OpenAPI 3.1.</p>



<figure class="wp-block-table"><table><thead><tr><th>Piece</th><th>.NET 8 template</th><th>.NET 9 / .NET 10 template</th></tr></thead><tbody><tr><td>Document generator</td><td>Swashbuckle (AddSwaggerGen)</td><td>Microsoft.AspNetCore.OpenApi (AddOpenApi)</td></tr><tr><td>Default document URL</td><td>/swagger/v1/swagger.json</td><td>/openapi/v1.json</td></tr><tr><td>OpenAPI version</td><td>3.0</td><td>3.0 in .NET 9, 3.1 in .NET 10</td></tr><tr><td>Built-in UI</td><td>Swagger UI at /swagger</td><td>None, you pick Scalar, Swagger UI or ReDoc</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">If you&#8217;re moving an older project, the <a href="https://aspdotnethelp.com/upgrade-dotnet-8-to-dotnet-10/">.NET 8 to .NET 10 upgrade guide</a> covers the rest of the jump. Swashbuckle still works, so nothing forces you to rip it out on day one.</p>



<h2 class="wp-block-heading">Before You Start the OpenAPI Build</h2>



<p class="wp-block-paragraph">You need the .NET 10 SDK (I used 10.0.402) and VS Code or Visual Studio. Not sure what&#8217;s installed? <a href="https://aspdotnethelp.com/check-dotnet-version/">Check your .NET version</a> first. The sample API serves four orders from an in-memory list:</p>



<figure class="wp-block-table"><table><thead><tr><th>Id</th><th>Customer</th><th>City</th><th>State</th><th>Total (USD)</th></tr></thead><tbody><tr><td>1001</td><td>Jenna Morales</td><td>Austin</td><td>TX</td><td>$249.99</td></tr><tr><td>1002</td><td>Tyler Brooks</td><td>Denver</td><td>CO</td><td>$89.50</td></tr><tr><td>1003</td><td>Rachel Kim</td><td>Seattle</td><td>WA</td><td>$412.00</td></tr><tr><td>1004</td><td>Marcus Reed</td><td>Dallas</td><td>TX</td><td>$156.25</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">These are the NuGet packages and exact versions this build uses (current stable on November 1, 2026):</p>



<figure class="wp-block-table"><table><thead><tr><th>Package</th><th>Version</th><th>Used for</th></tr></thead><tbody><tr><td>Microsoft.AspNetCore.OpenApi</td><td>10.0.14</td><td>Generates the document (in the template)</td></tr><tr><td>Scalar.AspNetCore</td><td>2.18.7</td><td>Scalar UI</td></tr><tr><td>Swashbuckle.AspNetCore.SwaggerUI</td><td>10.3.1</td><td>Swagger UI only, no generator</td></tr><tr><td>Microsoft.AspNetCore.Authentication.JwtBearer</td><td>10.0.14</td><td>Bearer token auth</td></tr><tr><td>Microsoft.Extensions.ApiDescription.Server</td><td>10.0.14</td><td>Build-time document</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Step 1: Create the Web API and Open /openapi/v1.json</h2>



<p class="wp-block-paragraph">Create the project from the template. It already references Microsoft.AspNetCore.OpenApi (the template pinned 10.0.13, so I bumped it):</p>



<pre class="wp-block-code"><code>dotnet new webapi -n OrdersApi -o OrdersApi
cd OrdersApi
dotnet add package Microsoft.AspNetCore.OpenApi --version 10.0.14</code></pre>



<p class="wp-block-paragraph">Replace the weather sample in <code>Program.cs</code> with the orders endpoints. The two OpenAPI lines are exactly what the template gives you, plus a YAML route:</p>



<pre class="wp-block-code"><code>var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();                                  // /openapi/v1.json
    app.MapOpenApi(&quot;/openapi/{documentName}.yaml&quot;);    // /openapi/v1.yaml
}

app.MapGet(&quot;/orders&quot;, (string? state) =&gt;
    OrderStore.Orders.Where(o =&gt; state is null || o.State == state));

app.MapGet(&quot;/orders/{id:int}&quot;, (int id) =&gt;
    OrderStore.Orders.FirstOrDefault(o =&gt; o.Id == id) is { } order
        ? Results.Ok(order)
        : Results.NotFound());

app.Run();

static class OrderStore
{
    public static readonly List&lt;Order&gt; Orders =
    [
        new(1001, &quot;Jenna Morales&quot;, &quot;Austin&quot;, &quot;TX&quot;, 249.99m),
        new(1002, &quot;Tyler Brooks&quot;, &quot;Denver&quot;, &quot;CO&quot;, 89.50m),
        new(1003, &quot;Rachel Kim&quot;, &quot;Seattle&quot;, &quot;WA&quot;, 412.00m),
        new(1004, &quot;Marcus Reed&quot;, &quot;Dallas&quot;, &quot;TX&quot;, 156.25m),
    ];
}

record Order(int Id, string Customer, string City, string State, decimal Total);</code></pre>



<p class="wp-block-paragraph">I trimmed <code>Properties/launchSettings.json</code> to one http profile on port 5280. It&#8217;s also where the Development environment comes from:</p>



<pre class="wp-block-code"><code>{
  &quot;$schema&quot;: &quot;https://json.schemastore.org/launchsettings.json&quot;,
  &quot;profiles&quot;: {
    &quot;http&quot;: {
      &quot;commandName&quot;: &quot;Project&quot;,
      &quot;dotnetRunMessages&quot;: true,
      &quot;launchBrowser&quot;: false,
      &quot;applicationUrl&quot;: &quot;http://localhost:5280&quot;,
      &quot;environmentVariables&quot;: {
        &quot;ASPNETCORE_ENVIRONMENT&quot;: &quot;Development&quot;
      }
    }
  }
}</code></pre>



<p class="wp-block-paragraph">I also deleted <code>appsettings.Development.json</code> and the <code>.http</code> file. This is the <code>appsettings.json</code> the template left:</p>



<pre class="wp-block-code"><code>{
  &quot;Logging&quot;: {
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Information&quot;,
      &quot;Microsoft.AspNetCore&quot;: &quot;Warning&quot;
    }
  },
  &quot;AllowedHosts&quot;: &quot;*&quot;
}</code></pre>



<p class="wp-block-paragraph">Run <code>dotnet run</code>. Here&#8217;s the startup log:</p>



<pre class="wp-block-code"><code>Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5280
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\DotNetDemo\OpenApiScalarDemo\OrdersApi</code></pre>



<p class="wp-block-paragraph">Now open <code>http://localhost:5280/openapi/v1.json</code>. That&#8217;s the default OpenAPI URL, <code>/openapi/{documentName}.json</code> with the document name <code>v1</code>. The top of the response:</p>



<pre class="wp-block-code"><code>{
  &quot;openapi&quot;: &quot;3.1.1&quot;,
  &quot;info&quot;: {
    &quot;title&quot;: &quot;OrdersApi | v1&quot;,
    &quot;version&quot;: &quot;1.0.0&quot;
  },
  &quot;servers&quot;: [
    {
      &quot;url&quot;: &quot;http://localhost:5280/&quot;
    }
  ],
  &quot;paths&quot;: {</code></pre>



<p class="wp-block-paragraph">Raw JSON gets old fast, so I wrote a tiny checker. It&#8217;s a file-based app next to the project folder, not inside it, or the project would compile it too.</p>



<p class="wp-block-paragraph">It uses <code>JsonDocument</code> and a little <a href="https://aspdotnethelp.com/linq-in-csharp/">LINQ in C#</a> to print one line per operation:</p>



<pre class="wp-block-code"><code>using System.Text.Json;

var http = new HttpClient { BaseAddress = new Uri(&quot;http://localhost:5280&quot;) };
var doc = JsonDocument.Parse(await http.GetStringAsync(&quot;/openapi/v1.json&quot;)).RootElement;
Console.WriteLine($&quot;OpenAPI {doc.GetProperty(&quot;openapi&quot;)} | {doc.GetProperty(&quot;info&quot;).GetProperty(&quot;title&quot;)}&quot;);

foreach (var path in doc.GetProperty(&quot;paths&quot;).EnumerateObject())
    foreach (var op in path.Value.EnumerateObject())
    {
        var codes = op.Value.GetProperty(&quot;responses&quot;).EnumerateObject().Select(r =&gt; r.Name);
        var summary = op.Value.TryGetProperty(&quot;summary&quot;, out var s) ? s.GetString() : &quot;-&quot;;
        var locked = op.Value.TryGetProperty(&quot;security&quot;, out _) ? &quot; [Bearer]&quot; : &quot;&quot;;
        Console.WriteLine($&quot;{op.Name.ToUpper(),-5}{path.Name,-14}{string.Join(&quot;,&quot;, codes),-9}{summary}{locked}&quot;);
    }

if (doc.GetProperty(&quot;components&quot;).TryGetProperty(&quot;securitySchemes&quot;, out var schemes))
    foreach (var sc in schemes.EnumerateObject())
        Console.WriteLine($&quot;Security scheme: {sc.Name} = {sc.Value.GetProperty(&quot;type&quot;)} {sc.Value.GetProperty(&quot;scheme&quot;)}&quot;);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>OpenAPI 3.1.1 | OrdersApi | v1
GET  /orders       200      -
GET  /orders/{id}  200      -</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/11/aspnet-core-openapi-v1-json-default-document-checkdocs.png" alt="ASP.NET Core OpenAPI default URL: CheckDocs.cs in VS Code reads http://localhost:5280/openapi/v1.json and prints OpenAPI 3.1.1, the OrdersApi | v1 title and GET /orders and GET /orders/{id}" class="wp-image-2686" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/11/aspnet-core-openapi-v1-json-default-document-checkdocs.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/11/aspnet-core-openapi-v1-json-default-document-checkdocs-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/11/aspnet-core-openapi-v1-json-default-document-checkdocs-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/11/aspnet-core-openapi-v1-json-default-document-checkdocs-768x674.png 768w" sizes="(max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">The default OpenAPI 3.1.1 document at /openapi/v1.json, read by CheckDocs.cs</figcaption></figure>
</div>


<p class="wp-block-paragraph">Two things stand out. The document is OpenAPI <code>3.1.1</code> with no extra setting, and <code>GET /orders/{id}</code> lists only a 200 response with no schema, because <code>Results.Ok</code> returns a plain <code>IResult</code>. We&#8217;ll fix that in Step 3.</p>



<p class="wp-block-paragraph">The YAML route works too. <code>/openapi/v1.yaml</code> returned 200:</p>



<pre class="wp-block-code"><code>openapi: &#x27;3.1.1&#x27;
info:
  title: OrdersApi | v1
  version: 1.0.0
servers:
  - url: http://localhost:5280/
paths:
  /orders:</code></pre>



<p class="wp-block-paragraph"><strong>Checkpoint:</strong> <code>/openapi/v1.json</code> returns 200, CheckDocs prints <code>OpenAPI 3.1.1 | OrdersApi | v1</code>, and <code>/swagger</code> still returns 404.</p>



<h2 class="wp-block-heading">Step 2: Add Scalar with MapScalarApiReference</h2>



<p class="wp-block-paragraph">Scalar is an open-source OpenAPI UI that Microsoft&#8217;s docs show next to Swagger UI. Add the package, then one <code>using</code> and one line inside the Development block:</p>



<pre class="wp-block-code"><code>dotnet add package Scalar.AspNetCore --version 2.18.7</code></pre>



<pre class="wp-block-code"><code>using Scalar.AspNetCore;

// ...
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();                                  // /openapi/v1.json
    app.MapOpenApi(&quot;/openapi/{documentName}.yaml&quot;);    // /openapi/v1.yaml
    app.MapScalarApiReference();                       // /scalar
}</code></pre>



<p class="wp-block-paragraph">Restart and browse to <code>/scalar/v1</code>. In my run <code>/scalar</code> answered 302 and redirected to <code>/scalar/</code>, and <code>/scalar/v1</code> returned 200:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1280" height="860" src="https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-scalar-aspnet-core-mapscalarapireference-first-run.png" alt="OpenAPI Scalar UI in ASP.NET Core: the first run of MapScalarApiReference shows OrdersApi v1 with two GET /orders endpoints and no summaries" class="wp-image-2689" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-scalar-aspnet-core-mapscalarapireference-first-run.png 1280w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-scalar-aspnet-core-mapscalarapireference-first-run-300x202.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-scalar-aspnet-core-mapscalarapireference-first-run-1024x688.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-scalar-aspnet-core-mapscalarapireference-first-run-768x516.png 768w" sizes="(max-width: 1280px) 100vw, 1280px" /><figcaption class="wp-element-caption">Scalar on the first run: it works, but the endpoints have no names yet</figcaption></figure>
</div>


<p class="wp-block-paragraph">Scalar reads the same /openapi/v1.json, so whatever your document lacks, your page lacks. Right now you&#8217;re missing titles, descriptions and response types.</p>



<p class="wp-block-paragraph"><strong>Checkpoint:</strong> Scalar shows <code>OrdersApi | v1</code> and two GET endpoints.</p>



<h2 class="wp-block-heading">Step 3: Describe OpenAPI Endpoints with WithSummary, TypedResults and XML Comments</h2>



<p class="wp-block-paragraph">This step changes the document, not the UI. I grouped the routes with <code>MapGroup</code> and <code>WithTags</code>, gave each one <code>WithSummary</code> or <code>WithDescription</code>, and switched to <code>TypedResults</code> so the response types show up. A document transformer sets the title:</p>



<pre class="wp-block-code"><code>builder.Services.AddOpenApi(options =&gt;
{
    options.AddDocumentTransformer((document, context, ct) =&gt;
    {
        document.Info = new() { Title = &quot;Orders API&quot;, Version = &quot;v1&quot;,
            Description = &quot;Orders for a small US outdoor-gear store.&quot; };
        return Task.CompletedTask;
    });
});</code></pre>



<pre class="wp-block-code"><code>var api = app.MapGroup(&quot;/orders&quot;).WithTags(&quot;Orders&quot;);

api.MapGet(&quot;/&quot;, (string? state) =&gt;
        TypedResults.Ok(OrderStore.Orders.Where(o =&gt; state is null || o.State == state)))
   .WithSummary(&quot;List orders&quot;)
   .WithDescription(&quot;Returns every order, or only the orders shipped to one US state (TX, CO, WA).&quot;);

api.MapGet(&quot;/{id:int}&quot;, OrderStore.GetOrderById);

api.MapPost(&quot;/&quot;, (NewOrder input) =&gt;
    {
        var order = new Order(OrderStore.Orders.Max(o =&gt; o.Id) + 1,
            input.Customer, input.City, input.State, input.Total);
        OrderStore.Orders.Add(order);
        return TypedResults.Created($&quot;/orders/{order.Id}&quot;, order);
    })
   .WithSummary(&quot;Create an order&quot;);</code></pre>



<p class="wp-block-paragraph"><code>GET /orders/{id}</code> is a named method, so I documented it with XML comments instead. In .NET 10 a source generator copies them into the document when the project has <code>&lt;GenerateDocumentationFile&gt;true&lt;/GenerateDocumentationFile&gt;</code> (it&#8217;s in the csproj in Step 7):</p>



<pre class="wp-block-code"><code>static class OrderStore
{
    // ... Orders list from Step 1 ...

/// &lt;summary&gt;Get one order by its number.&lt;/summary&gt;
/// &lt;param name=&quot;id&quot;&gt;The order number, for example 1001.&lt;/param&gt;
/// &lt;response code=&quot;200&quot;&gt;The order was found.&lt;/response&gt;
/// &lt;response code=&quot;404&quot;&gt;No order has that number.&lt;/response&gt;
public static Results&lt;Ok&lt;Order&gt;, NotFound&gt; GetOrderById(int id) =&gt;
    Orders.FirstOrDefault(o =&gt; o.Id == id) is { } order
        ? TypedResults.Ok(order)
        : TypedResults.NotFound();
}</code></pre>



<p class="wp-block-paragraph">The return type <code>Results&lt;Ok&lt;Order&gt;, NotFound&gt;</code> tells OpenAPI about both the 200 and the 404, and the <code>response</code> tags only add the descriptions. The checker now prints:</p>



<pre class="wp-block-code"><code>OpenAPI 3.1.1 | Orders API
GET  /orders       200      List orders
POST /orders       201      Create an order
GET  /orders/{id}  200,404  Get one order by its number.</code></pre>



<p class="wp-block-paragraph">One surprise in the schema: <code>decimal Total</code> (and <code>int Id</code>) came out as a number <em>or</em> a string, because ASP.NET Core&#8217;s web JSON defaults accept numbers in quotes. Here&#8217;s the real snippet:</p>



<pre class="wp-block-code"><code>&quot;total&quot;: {
  &quot;pattern&quot;: &quot;^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$&quot;,
  &quot;type&quot;: [
    &quot;number&quot;,
    &quot;string&quot;
  ],
  &quot;format&quot;: &quot;double&quot;
}</code></pre>



<p class="wp-block-paragraph">If you&#8217;d rather send plain numbers, tune the serializer options; my notes on <a href="https://aspdotnethelp.com/convert-object-to-json-without-escape-characters-in-csharp/">System.Text.Json options for clean JSON</a> cover the same settings.</p>



<p class="wp-block-paragraph"><strong>Checkpoint:</strong> every operation has a summary, and <code>GET /orders/{id}</code> lists <code>200,404</code>.</p>



<h2 class="wp-block-heading">Step 4: Add Swagger UI to the .NET 10 OpenAPI Document (Optional)</h2>



<p class="wp-block-paragraph">Prefer the classic Swagger page? You don&#8217;t need all of Swashbuckle. <code>Swashbuckle.AspNetCore.SwaggerUI</code> is just the UI, and you point it at the document ASP.NET Core already generates:</p>



<pre class="wp-block-code"><code>dotnet add package Swashbuckle.AspNetCore.SwaggerUI --version 10.3.1</code></pre>



<pre class="wp-block-code"><code>app.UseSwaggerUI(o =&gt; o.SwaggerEndpoint(&quot;/openapi/v1.json&quot;, &quot;Orders API v1&quot;));</code></pre>



<p class="wp-block-paragraph">Put that line inside the same Development block. In my run <code>/swagger</code> returned 301 (a redirect) and <code>/swagger/index.html</code> returned 200:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1280" height="860" src="https://aspdotnethelp.com/wp-content/uploads/2026/11/swagger-ui-dotnet-10-swashbuckle-aspnetcore-swaggerui-openapi-v1-json.png" alt="Swagger in .NET 10: Swagger UI from Swashbuckle.AspNetCore.SwaggerUI reading /openapi/v1.json and showing Orders API v1 with GET /orders, POST /orders and GET /orders/{id}" class="wp-image-2690" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/11/swagger-ui-dotnet-10-swashbuckle-aspnetcore-swaggerui-openapi-v1-json.png 1280w, https://aspdotnethelp.com/wp-content/uploads/2026/11/swagger-ui-dotnet-10-swashbuckle-aspnetcore-swaggerui-openapi-v1-json-300x202.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/11/swagger-ui-dotnet-10-swashbuckle-aspnetcore-swaggerui-openapi-v1-json-1024x688.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/11/swagger-ui-dotnet-10-swashbuckle-aspnetcore-swaggerui-openapi-v1-json-768x516.png 768w" sizes="auto, (max-width: 1280px) 100vw, 1280px" /><figcaption class="wp-element-caption">Swagger UI on top of the built-in OpenAPI document</figcaption></figure>
</div>


<p class="wp-block-paragraph">Don&#8217;t call <code>AddSwaggerGen</code> or <code>UseSwagger</code> here. They&#8217;d generate a second document from Swashbuckle, and you&#8217;d have two sources of truth. Running Scalar and Swagger UI side by side is fine, since both read the same JSON.</p>



<p class="wp-block-paragraph"><strong>Checkpoint:</strong> Swagger UI lists the same three operations and summaries as Scalar.</p>



<h2 class="wp-block-heading">Step 5: Keep the OpenAPI Docs in Development Only</h2>



<p class="wp-block-paragraph">Every docs line in this build sits inside <code>if (app.Environment.IsDevelopment())</code>. Microsoft recommends that for Swagger UI, ReDoc and Scalar, so you don&#8217;t hand your API map to the internet. I ran the final app with <code>ASPNETCORE_ENVIRONMENT=Production</code> on port 5281 to prove it:</p>



<pre class="wp-block-code"><code>404  /openapi/v1.json
404  /scalar/v1
404  /swagger/index.html
200  /orders</code></pre>



<p class="wp-block-paragraph">The docs vanish and the API keeps working. If a staging site needs docs, add a check for that environment name rather than deleting the <code>if</code>.</p>



<p class="wp-block-paragraph"><strong>Checkpoint:</strong> all three docs URLs return 404 in Production while <code>/orders</code> returns 200.</p>



<h2 class="wp-block-heading">Step 6: Add OpenAPI Authentication with a Bearer Security Scheme</h2>



<p class="wp-block-paragraph">Next, lock <code>POST /orders</code> behind a JWT. Two parts, and people usually forget the second: the app has to require the token, and the OpenAPI document has to say so, or Scalar&#8217;s token box does nothing.</p>



<pre class="wp-block-code"><code>dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 10.0.14</code></pre>



<p class="wp-block-paragraph">In .NET 10 the OpenAPI types live in the <code>Microsoft.OpenApi</code> namespace (it was <code>Microsoft.OpenApi.Models</code> in .NET 9). A document transformer adds the scheme, written as an <a href="https://aspdotnethelp.com/interface-in-csharp/">interface implementation</a>:</p>



<pre class="wp-block-code"><code>sealed class BearerSecuritySchemeTransformer : IOpenApiDocumentTransformer
{
    public Task TransformAsync(OpenApiDocument document,
        OpenApiDocumentTransformerContext context, CancellationToken ct)
    {
        document.Components ??= new OpenApiComponents();
        document.Components.SecuritySchemes = new Dictionary&lt;string, IOpenApiSecurityScheme&gt;
        {
            [&quot;Bearer&quot;] = new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                Scheme = &quot;bearer&quot;,
                In = ParameterLocation.Header,
                BearerFormat = &quot;JWT&quot;
            }
        };
        return Task.CompletedTask;
    }
}</code></pre>



<p class="wp-block-paragraph">An operation transformer then marks only the endpoints that carry authorization metadata, so the public GETs stay open in the docs:</p>



<pre class="wp-block-code"><code>builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services.AddOpenApi(options =&gt;
{
    // ... Info transformer from Step 3 ...
options.AddDocumentTransformer&lt;BearerSecuritySchemeTransformer&gt;();
options.AddOperationTransformer((operation, context, ct) =&gt;
{
    bool needsToken = context.Description.ActionDescriptor.EndpointMetadata
        .OfType&lt;IAuthorizeData&gt;().Any();
    if (needsToken)
    {
        operation.Security ??= [];
        operation.Security.Add(new OpenApiSecurityRequirement
        {
            [new OpenApiSecuritySchemeReference(&quot;Bearer&quot;, context.Document)] = []
        });
    }
    return Task.CompletedTask;
});
});</code></pre>



<p class="wp-block-paragraph">Finally, <code>.Produces(StatusCodes.Status401Unauthorized)</code> and <code>.RequireAuthorization()</code> go on the POST, and <code>AddPreferredSecuritySchemes("Bearer")</code> makes Scalar preselect it. Here&#8217;s the whole <code>Program.cs</code> at this point:</p>



<pre class="wp-block-code"><code>using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi;
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services.AddOpenApi(options =&gt;
{
    options.AddDocumentTransformer((document, context, ct) =&gt;
    {
        document.Info = new() { Title = &quot;Orders API&quot;, Version = &quot;v1&quot;,
            Description = &quot;Orders for a small US outdoor-gear store.&quot; };
        return Task.CompletedTask;
    });
options.AddDocumentTransformer&lt;BearerSecuritySchemeTransformer&gt;();
options.AddOperationTransformer((operation, context, ct) =&gt;
{
    bool needsToken = context.Description.ActionDescriptor.EndpointMetadata
        .OfType&lt;IAuthorizeData&gt;().Any();
    if (needsToken)
    {
        operation.Security ??= [];
        operation.Security.Add(new OpenApiSecurityRequirement
        {
            [new OpenApiSecuritySchemeReference(&quot;Bearer&quot;, context.Document)] = []
        });
    }
    return Task.CompletedTask;
});
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();                                  // /openapi/v1.json
    app.MapOpenApi(&quot;/openapi/{documentName}.yaml&quot;);    // /openapi/v1.yaml
    app.MapScalarApiReference(o =&gt; o.AddPreferredSecuritySchemes(&quot;Bearer&quot;));
    app.UseSwaggerUI(o =&gt; o.SwaggerEndpoint(&quot;/openapi/v1.json&quot;, &quot;Orders API v1&quot;));
}

var api = app.MapGroup(&quot;/orders&quot;).WithTags(&quot;Orders&quot;);

api.MapGet(&quot;/&quot;, (string? state) =&gt;
        TypedResults.Ok(OrderStore.Orders.Where(o =&gt; state is null || o.State == state)))
   .WithSummary(&quot;List orders&quot;)
   .WithDescription(&quot;Returns every order, or only the orders shipped to one US state (TX, CO, WA).&quot;);

api.MapGet(&quot;/{id:int}&quot;, OrderStore.GetOrderById);

api.MapPost(&quot;/&quot;, (NewOrder input) =&gt;
    {
        var order = new Order(OrderStore.Orders.Max(o =&gt; o.Id) + 1,
            input.Customer, input.City, input.State, input.Total);
        OrderStore.Orders.Add(order);
        return TypedResults.Created($&quot;/orders/{order.Id}&quot;, order);
    })
   .WithSummary(&quot;Create an order&quot;)
   .Produces(StatusCodes.Status401Unauthorized)
   .RequireAuthorization();

app.Run();

static class OrderStore
{
    public static readonly List&lt;Order&gt; Orders =
    [
        new(1001, &quot;Jenna Morales&quot;, &quot;Austin&quot;, &quot;TX&quot;, 249.99m),
        new(1002, &quot;Tyler Brooks&quot;, &quot;Denver&quot;, &quot;CO&quot;, 89.50m),
        new(1003, &quot;Rachel Kim&quot;, &quot;Seattle&quot;, &quot;WA&quot;, 412.00m),
        new(1004, &quot;Marcus Reed&quot;, &quot;Dallas&quot;, &quot;TX&quot;, 156.25m),
    ];

/// &lt;summary&gt;Get one order by its number.&lt;/summary&gt;
/// &lt;param name=&quot;id&quot;&gt;The order number, for example 1001.&lt;/param&gt;
/// &lt;response code=&quot;200&quot;&gt;The order was found.&lt;/response&gt;
/// &lt;response code=&quot;404&quot;&gt;No order has that number.&lt;/response&gt;
public static Results&lt;Ok&lt;Order&gt;, NotFound&gt; GetOrderById(int id) =&gt;
    Orders.FirstOrDefault(o =&gt; o.Id == id) is { } order
        ? TypedResults.Ok(order)
        : TypedResults.NotFound();
}

record Order(int Id, string Customer, string City, string State, decimal Total);
record NewOrder(string Customer, string City, string State, decimal Total);

sealed class BearerSecuritySchemeTransformer : IOpenApiDocumentTransformer
{
    public Task TransformAsync(OpenApiDocument document,
        OpenApiDocumentTransformerContext context, CancellationToken ct)
    {
        document.Components ??= new OpenApiComponents();
        document.Components.SecuritySchemes = new Dictionary&lt;string, IOpenApiSecurityScheme&gt;
        {
            [&quot;Bearer&quot;] = new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                Scheme = &quot;bearer&quot;,
                In = ParameterLocation.Header,
                BearerFormat = &quot;JWT&quot;
            }
        };
        return Task.CompletedTask;
    }
}</code></pre>



<pre class="wp-block-code"><code>using System.Text.Json;

var http = new HttpClient { BaseAddress = new Uri(&quot;http://localhost:5280&quot;) };
var doc = JsonDocument.Parse(await http.GetStringAsync(&quot;/openapi/v1.json&quot;)).RootElement;
Console.WriteLine($&quot;OpenAPI {doc.GetProperty(&quot;openapi&quot;)} | {doc.GetProperty(&quot;info&quot;).GetProperty(&quot;title&quot;)}&quot;);

foreach (var path in doc.GetProperty(&quot;paths&quot;).EnumerateObject())
    foreach (var op in path.Value.EnumerateObject())
    {
        var codes = op.Value.GetProperty(&quot;responses&quot;).EnumerateObject().Select(r =&gt; r.Name);
        var summary = op.Value.TryGetProperty(&quot;summary&quot;, out var s) ? s.GetString() : &quot;-&quot;;
        var locked = op.Value.TryGetProperty(&quot;security&quot;, out _) ? &quot; [Bearer]&quot; : &quot;&quot;;
        Console.WriteLine($&quot;{op.Name.ToUpper(),-5}{path.Name,-14}{string.Join(&quot;,&quot;, codes),-9}{summary}{locked}&quot;);
    }

if (doc.GetProperty(&quot;components&quot;).TryGetProperty(&quot;securitySchemes&quot;, out var schemes))
    foreach (var sc in schemes.EnumerateObject())
        Console.WriteLine($&quot;Security scheme: {sc.Name} = {sc.Value.GetProperty(&quot;type&quot;)} {sc.Value.GetProperty(&quot;scheme&quot;)}&quot;);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>OpenAPI 3.1.1 | Orders API
GET  /orders       200      List orders
POST /orders       201,401  Create an order [Bearer]
GET  /orders/{id}  200,404  Get one order by its number.
Security scheme: Bearer = http bearer</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-authentication-bearer-security-scheme-document-transformer.png" alt="OpenAPI authentication in ASP.NET Core: CheckDocs.cs output shows POST /orders with responses 201,401 marked [Bearer] and Security scheme: Bearer = http bearer, while the GET endpoints stay open" class="wp-image-2687" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-authentication-bearer-security-scheme-document-transformer.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-authentication-bearer-security-scheme-document-transformer-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-authentication-bearer-security-scheme-document-transformer-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-authentication-bearer-security-scheme-document-transformer-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">The document now declares the Bearer scheme, and only POST /orders requires it</figcaption></figure>
</div>


<p class="wp-block-paragraph"><strong>Checkpoint:</strong> <code>[Bearer]</code> appears on <code>POST /orders</code> only, and the last line reads <code>Security scheme: Bearer = http bearer</code>.</p>



<h2 class="wp-block-heading">Step 7: Generate the OpenAPI Document at Build Time</h2>



<p class="wp-block-paragraph">Client generators and CI linters want a file, not a running server. Add <code>Microsoft.Extensions.ApiDescription.Server</code> and turn on <code>OpenApiGenerateDocumentsOnBuild</code>. Here&#8217;s the final project file:</p>



<pre class="wp-block-code"><code>dotnet add package Microsoft.Extensions.ApiDescription.Server --version 10.0.14</code></pre>



<pre class="wp-block-code"><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk.Web&quot;&gt;

  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
    &lt;GenerateDocumentationFile&gt;true&lt;/GenerateDocumentationFile&gt;
    &lt;OpenApiGenerateDocumentsOnBuild&gt;true&lt;/OpenApiGenerateDocumentsOnBuild&gt;
  &lt;/PropertyGroup&gt;

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.Authentication.JwtBearer&quot; Version=&quot;10.0.14&quot; /&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.OpenApi&quot; Version=&quot;10.0.14&quot; /&gt;
    &lt;PackageReference Include=&quot;Microsoft.Extensions.ApiDescription.Server&quot; Version=&quot;10.0.14&quot;&gt;
      &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers; buildtransitive&lt;/IncludeAssets&gt;
      &lt;PrivateAssets&gt;all&lt;/PrivateAssets&gt;
    &lt;/PackageReference&gt;
    &lt;PackageReference Include=&quot;Scalar.AspNetCore&quot; Version=&quot;2.18.7&quot; /&gt;
    &lt;PackageReference Include=&quot;Swashbuckle.AspNetCore.SwaggerUI&quot; Version=&quot;10.3.1&quot; /&gt;
  &lt;/ItemGroup&gt;

&lt;/Project&gt;</code></pre>



<p class="wp-block-paragraph"><code>dotnet build</code> now starts the app&#8217;s host briefly and writes <code>obj/OrdersApi.json</code>. In the shot I deleted the file and rebuilt with <code>--no-incremental</code>, so you can see it come back:</p>



<pre class="wp-block-code"><code>del obj\OrdersApi.json &amp; dotnet build --no-incremental -v:m -tl:off -nologo &amp;&amp; findstr /n openapi obj\OrdersApi.json</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-generate-document-at-build-time-apidescription-server.png" alt="OpenAPI document at build time in .NET 10: VS Code shows OrdersApi.csproj with Microsoft.Extensions.ApiDescription.Server and OpenApiGenerateDocumentsOnBuild, and dotnet build writes obj\OrdersApi.json with openapi 3.1.1" class="wp-image-2688" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-generate-document-at-build-time-apidescription-server.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-generate-document-at-build-time-apidescription-server-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-generate-document-at-build-time-apidescription-server-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/11/openapi-generate-document-at-build-time-apidescription-server-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">dotnet build writes the OpenAPI document to obj\OrdersApi.json</figcaption></figure>
</div>


<p class="wp-block-paragraph">The first lines of the generated file match what you saw from the live endpoint:</p>



<pre class="wp-block-code"><code>{
  &quot;openapi&quot;: &quot;3.1.1&quot;,
  &quot;info&quot;: {
    &quot;title&quot;: &quot;Orders API&quot;,
    &quot;description&quot;: &quot;Orders for a small US outdoor-gear store.&quot;,
    &quot;version&quot;: &quot;v1&quot;</code></pre>



<p class="wp-block-paragraph">Want it next to the csproj instead? Set <code>OpenApiDocumentsDirectory</code> to <code>$(MSBuildProjectDirectory)</code>. YAML output at build time isn&#8217;t supported yet in .NET 10, so build-time files are JSON.</p>



<p class="wp-block-paragraph"><strong>Checkpoint:</strong> <code>obj/OrdersApi.json</code> exists after the build and starts with <code>"openapi": "3.1.1"</code>.</p>



<h2 class="wp-block-heading">Test the OpenAPI Bearer Lock and Endpoints</h2>



<p class="wp-block-paragraph">The docs say POST needs a token, so the API had better agree. Posting an order for Ava Thompson in Phoenix without one:</p>



<pre class="wp-block-code"><code>curl.exe -i -X POST http://localhost:5280/orders -H &quot;Content-Type: application/json&quot; -d &quot;{\&quot;customer\&quot;:\&quot;Ava Thompson\&quot;,\&quot;city\&quot;:\&quot;Phoenix\&quot;,\&quot;state\&quot;:\&quot;AZ\&quot;,\&quot;total\&quot;:74.10}&quot;</code></pre>



<pre class="wp-block-code"><code>HTTP/1.1 401 Unauthorized
Content-Length: 0
Server: Kestrel
WWW-Authenticate: Bearer</code></pre>



<p class="wp-block-paragraph">A 401 with <code>WWW-Authenticate: Bearer</code>, exactly as documented. The open endpoints still answer; <code>GET /orders?state=TX</code> returns the two Texas orders:</p>



<pre class="wp-block-code"><code>[{&quot;id&quot;:1001,&quot;customer&quot;:&quot;Jenna Morales&quot;,&quot;city&quot;:&quot;Austin&quot;,&quot;state&quot;:&quot;TX&quot;,&quot;total&quot;:249.99},{&quot;id&quot;:1004,&quot;customer&quot;:&quot;Marcus Reed&quot;,&quot;city&quot;:&quot;Dallas&quot;,&quot;state&quot;:&quot;TX&quot;,&quot;total&quot;:156.25}]</code></pre>



<p class="wp-block-paragraph">And <code>GET /orders/2001</code> returned 404, the 404 the XML comment describes. A filter like this is plain <a href="https://aspdotnethelp.com/list-in-csharp/">List&lt;T&gt; in C#</a> filtering, so swap in EF Core when the data gets real.</p>



<h2 class="wp-block-heading">Troubleshooting ASP.NET Core OpenAPI and Scalar</h2>



<p class="wp-block-paragraph"><strong>/swagger returns 404.</strong> Expected on .NET 9 and 10. Use <code>/openapi/v1.json</code> or <code>/scalar/v1</code>, or add the Swagger UI package from Step 4.</p>



<p class="wp-block-paragraph"><strong>The type IOpenApiDocumentTransformer could not be found.</strong> I hit this on my first build. The transformer types need <code>using Microsoft.AspNetCore.OpenApi;</code>, and the model types need <code>using Microsoft.OpenApi;</code> in .NET 10.</p>



<p class="wp-block-paragraph"><strong>Scalar ignores the token you paste.</strong> The document has no security scheme. Add the transformer from Step 6 and check that <code>components.securitySchemes</code> appears in the JSON.</p>



<p class="wp-block-paragraph"><strong>XML comments don&#8217;t show up.</strong> They apply to named methods and types, not to inline lambdas, and need <code>GenerateDocumentationFile</code>. Use <code>WithSummary</code> for lambdas.</p>



<p class="wp-block-paragraph"><strong>Deleted obj/OrdersApi.json and the build didn&#8217;t recreate it.</strong> I hit this too: an up-to-date build skips the generator, so <code>findstr</code> couldn&#8217;t find the file. Rebuild with <code>dotnet build --no-incremental</code>.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> a 500 on <code>/openapi/v1.json</code> usually means an endpoint threw during generation; wrap that handler&#8217;s logic in a <a href="https://aspdotnethelp.com/try-catch-in-csharp/">try-catch in C#</a> while you find it, and read the server log.</p>



<h2 class="wp-block-heading">Where to Take It Next with ASP.NET Core OpenAPI</h2>



<p class="wp-block-paragraph">Add a sign-in flow that issues the JWT; my <a href="https://aspdotnethelp.com/generate-otp-asp-net/">OTP sign-in API in ASP.NET Core</a> is one way to get there. Then feed the build-time JSON to a client generator such as Kiota or NSwag.</p>



<p class="wp-block-paragraph">If production still runs .NET 8, check the <a href="https://aspdotnethelp.com/dotnet-8-9-end-of-support/">.NET 8 and .NET 9 end-of-support dates</a> before you plan the move to the built-in generator.</p>



<p class="wp-block-paragraph">More ASP.NET Core and .NET reading on the site:</p>



<ul class="wp-block-list">
<li><a href="https://aspdotnethelp.com/asp-net-core-tutorials/">All ASP.NET Core tutorials on AspDotnetHelp</a></li>

<li><a href="https://aspdotnethelp.com/dotnet-versions/">Every .NET version and what changed</a></li>

<li><a href="https://aspdotnethelp.com/dictionary-in-csharp/">Dictionary in C# for a keyed order lookup</a></li>

<li><a href="https://aspdotnethelp.com/create-a-console-application-in-visual-studio-code/">Create a console app in VS Code</a></li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-oas0000001" class="rank-math-list-item">
<h3 class="rank-math-question ">What is the default OpenAPI URL in ASP.NET Core?</h3>
<div class="rank-math-answer ">

<p><code>/openapi/{documentName}.json</code>, so <code>/openapi/v1.json</code> for the default document. Pass a different pattern to <code>MapOpenApi</code> to change it, for example <code>/openapi/{documentName}.yaml</code> for YAML.</p>

</div>
</div>
<div id="faq-question-oas0000002" class="rank-math-list-item">
<h3 class="rank-math-question ">Why was Swagger removed from ASP.NET Core?</h3>
<div class="rank-math-answer ">

<p>Swashbuckle was dropped from the templates in .NET 9 because ASP.NET Core now generates OpenAPI documents itself. Swagger UI isn&#8217;t banned; see the <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/using-openapi-documents?view=aspnetcore-10.0" target="_blank" rel="noopener">Use the generated OpenAPI documents guide on Microsoft Learn</a>.</p>

</div>
</div>
<div id="faq-question-oas0000003" class="rank-math-list-item">
<h3 class="rank-math-question ">Should I use Scalar or Swagger UI in .NET 10?</h3>
<div class="rank-math-answer ">

<p>Both read the same /openapi/v1.json, so it&#8217;s taste. I default to Scalar for the cleaner layout and built-in code samples, and keep Swagger UI when a team already knows it.</p>

</div>
</div>
<div id="faq-question-oas0000004" class="rank-math-list-item">
<h3 class="rank-math-question ">Does .NET 10 generate OpenAPI 3.1?</h3>
<div class="rank-math-answer ">

<p>Yes. .NET 10 produces OpenAPI 3.1 by default; my run returned 3.1.1. Set <code>options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0</code> in <code>AddOpenApi</code> for tools that only read 3.0.</p>

</div>
</div>
<div id="faq-question-oas0000005" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I add authentication to the OpenAPI document?</h3>
<div class="rank-math-answer ">

<p>Register the scheme in a document transformer under <code>components.securitySchemes</code>, then add a security requirement to the protected operations. Without it, Scalar and Swagger UI can&#8217;t send your token.</p>

</div>
</div>
<div id="faq-question-oas0000006" class="rank-math-list-item">
<h3 class="rank-math-question ">Is OpenAPI the same as Swagger?</h3>
<div class="rank-math-answer ">

<p>Swagger was the original name of the spec, renamed OpenAPI in 2016; &#8220;Swagger&#8221; now means the SmartBear tools. See the <a href="https://spec.openapis.org/oas/v3.1.1.html" target="_blank" rel="noopener">OpenAPI 3.1.1 specification</a>.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img alt='Bijay Kumar' src='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=100&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=200&#038;d=mm&#038;r=g 2x' class='avatar avatar-100 photo' height='100' width='100' itemprop="image"/></div><div class="saboxplugin-authorname"><a href="https://aspdotnethelp.com/bijay-kumar/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Bijay Kumar is a renowned software engineer, accomplished author, and distinguished <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000972" rel="nofollow noopener" target="_blank">Microsoft Most Valuable Professional</a> (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. <a href="https://aspdotnethelp.com/about/" rel="nofollow noopener">Read more&#8230;</a></p>
</div></div><div class="saboxplugin-web "><a href="https://aspdotnethelp.com" target="_self">aspdotnethelp.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials "><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-linkedin" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 448 512"><path fill="currentColor" d="M100.3 480H7.4V180.9h92.9V480zM53.8 140.1C24.1 140.1 0 115.5 0 85.8 0 56.1 24.1 32 53.8 32c29.7 0 53.8 24.1 53.8 53.8 0 29.7-24.1 54.3-53.8 54.3zM448 480h-92.7V334.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V480h-92.8V180.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V480z"></path></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 264 512"><path fill="currentColor" d="M76.7 512V283H0v-91h76.7v-71.7C76.7 42.4 124.3 0 193.8 0c33.3 0 61.9 2.5 70.2 3.6V85h-48.2c-37.8 0-45.1 18-45.1 44.3V192H256l-11.7 91h-73.6v229"></path></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/c/enjoysharepoint?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 30 30"><path d="M26.37,26l-8.795-12.822l0.015,0.012L25.52,4h-2.65l-6.46,7.48L11.28,4H4.33l8.211,11.971L12.54,15.97L3.88,26h2.65 l7.182-8.322L19.42,26H26.37z M10.23,6l12.34,18h-2.1L8.12,6H10.23z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Upgrade .NET 8 to .NET 10: ASP.NET Core Step by Step</title>
		<link>https://aspdotnethelp.com/upgrade-dotnet-8-to-dotnet-10/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 26 Oct 2026 18:48:58 +0000</pubDate>
				<category><![CDATA[ASP.NET Core]]></category>
		<guid isPermaLink="false">https://aspdotnethelp.com/?p=2044</guid>

					<description><![CDATA[Upgrading a .NET 8 app to .NET 10 took me one afternoon, and most of it was package work. With .NET 8 support ending November 10, 2026, now&#8217;s the time to move. The short answer: to upgrade .NET 8 to .NET 10, install the .NET 10 SDK, point global.json at it, change net8.0 to net10.0, ... <a title="How to Upgrade .NET 8 to .NET 10: ASP.NET Core Step by Step" class="read-more" href="https://aspdotnethelp.com/upgrade-dotnet-8-to-dotnet-10/" aria-label="Read more about How to Upgrade .NET 8 to .NET 10: ASP.NET Core Step by Step">Read more &#62;&#62;</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Upgrading a .NET 8 app to .NET 10 took me one afternoon, and most of it was package work. With .NET 8 support ending November 10, 2026, now&#8217;s the time to move.</p>



<p class="wp-block-paragraph">The short answer: to upgrade .NET 8 to .NET 10, install the .NET 10 SDK, point <code>global.json</code> at it, change <code>net8.0</code> to <code>net10.0</code>, update your Microsoft packages to 10.0.x, then fix the warnings and breaking changes the build shows you.</p>



<p class="wp-block-paragraph">In this tutorial, I&#8217;ll <strong>upgrade .NET 8 to .NET 10</strong> on a real ASP.NET Core Minimal API, step by step. You&#8217;ll see the app running on .NET 8, the crash I hit halfway through, the fix, and the same app running on .NET 10.</p>



<h2 class="wp-block-heading">What You Need Before You Upgrade .NET 8 to .NET 10</h2>



<p class="wp-block-paragraph">My demo is a small &#8220;US store orders&#8221; Minimal API. It uses EF Core with the InMemory provider, Swashbuckle for Swagger, and <code>Microsoft.AspNetCore.OpenApi</code>, all pinned at 8.x like a typical project created from the .NET 8 template.</p>



<p class="wp-block-paragraph">I work in VS Code on Windows 11. You need the .NET 10 SDK installed next to the .NET 8 one, and it doesn&#8217;t remove anything. If you&#8217;re not sure what&#8217;s on your machine, here&#8217;s how to <a href="https://aspdotnethelp.com/check-dotnet-version/">check which .NET version is installed</a>:</p>



<pre class="wp-block-code"><code>dotnet --list-sdks
dotnet --version</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>7.0.403 [C:\Program Files\dotnet\sdk]
8.0.427 [C:\Program Files\dotnet\sdk]
10.0.402 [C:\Program Files\dotnet\sdk]
8.0.427</code></pre>



<p class="wp-block-paragraph">The <code>--list-sdks</code> command ignores <code>global.json</code>, but <code>dotnet --version</code> doesn&#8217;t. That&#8217;s why it prints 8.0.427: my folder still pins the .NET 8 SDK.</p>



<figure class="wp-block-table"><table><thead><tr><th>Item</th><th>Before</th><th>After</th></tr></thead><tbody><tr><td>SDK in <code>global.json</code></td><td>8.0.427</td><td>10.0.100 (rolls forward to 10.0.402)</td></tr><tr><td><code>TargetFramework</code></td><td><code>net8.0</code></td><td><code>net10.0</code></td></tr><tr><td>Microsoft.AspNetCore.OpenApi</td><td>8.0.11</td><td>10.0.14</td></tr><tr><td>Microsoft.EntityFrameworkCore.InMemory</td><td>8.0.11</td><td>10.0.14</td></tr><tr><td>Swashbuckle.AspNetCore</td><td>6.6.2</td><td>Removed (built-in <code>MapOpenApi</code>)</td></tr><tr><td>Runtime</td><td>.NET 8.0.33</td><td>.NET 10.0.13</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Step 1: Run the ASP.NET Core App on .NET 8 First</h2>



<p class="wp-block-paragraph">Never start an upgrade from a broken build. I want proof of what works today, so I can compare after the change. Here&#8217;s the folder layout:</p>



<pre class="wp-block-code"><code>UpgradeDemo
├── global.json
└── UsStoreOrders
    ├── UsStoreOrders.csproj
    ├── Program.cs
    ├── appsettings.json
    └── UsStoreOrders.http</code></pre>



<p class="wp-block-paragraph">This is the .NET 8 <code>global.json</code>. The <code>rollForward</code> policy lets the CLI use a newer 8.0 feature band if one is installed:</p>



<pre class="wp-block-code"><code>{
  &quot;sdk&quot;: {
    &quot;version&quot;: &quot;8.0.427&quot;,
    &quot;rollForward&quot;: &quot;latestFeature&quot;
  }
}</code></pre>



<p class="wp-block-paragraph">And the project file, with the same package versions you&#8217;d find in a lot of 2024 projects:</p>



<pre class="wp-block-code"><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk.Web&quot;&gt;

  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net8.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
  &lt;/PropertyGroup&gt;

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.OpenApi&quot; Version=&quot;8.0.11&quot; /&gt;
    &lt;PackageReference Include=&quot;Microsoft.EntityFrameworkCore.InMemory&quot; Version=&quot;8.0.11&quot; /&gt;
    &lt;PackageReference Include=&quot;Swashbuckle.AspNetCore&quot; Version=&quot;6.6.2&quot; /&gt;
  &lt;/ItemGroup&gt;

&lt;/Project&gt;</code></pre>



<p class="wp-block-paragraph"><code>Program.cs</code> follows the .NET 8 Web API template: Swashbuckle for Swagger, <code>WithOpenApi()</code> on each endpoint, and an <code>/about</code> endpoint that reports the runtime with <code>RuntimeInformation.FrameworkDescription</code>:</p>



<pre class="wp-block-code"><code>using System.Runtime.InteropServices;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext&lt;StoreDb&gt;(o =&gt; o.UseInMemoryDatabase(&quot;orders&quot;));
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService&lt;StoreDb&gt;();
    db.Orders.AddRange(
        new Order(1001, &quot;Jenna Moore&quot;, &quot;Austin&quot;, &quot;TX&quot;, 249.99m),
        new Order(1002, &quot;Ryan Carter&quot;, &quot;Denver&quot;, &quot;CO&quot;, 89.50m),
        new Order(1003, &quot;Chloe Brady&quot;, &quot;Seattle&quot;, &quot;WA&quot;, 1325.00m));
    db.SaveChanges();
}

app.MapGet(&quot;/about&quot;, () =&gt; new
{
    Runtime = RuntimeInformation.FrameworkDescription,
    Target = AppContext.TargetFrameworkName
}).WithName(&quot;About&quot;).WithOpenApi();

app.MapGet(&quot;/orders&quot;, (StoreDb db) =&gt; db.Orders.OrderBy(o =&gt; o.Id).ToListAsync())
   .WithName(&quot;ListOrders&quot;).WithOpenApi();

app.MapGet(&quot;/orders/{id:int}&quot;, async (int id, StoreDb db) =&gt;
    await db.Orders.FindAsync(id) is Order o ? Results.Ok(o) : Results.NotFound())
   .WithName(&quot;GetOrder&quot;).WithOpenApi();

app.Run();

public record Order(int Id, string Customer, string City, string State, decimal Total);

public class StoreDb(DbContextOptions&lt;StoreDb&gt; options) : DbContext(options)
{
    public DbSet&lt;Order&gt; Orders =&gt; Set&lt;Order&gt;();
}</code></pre>



<p class="wp-block-paragraph">The <code>appsettings.json</code> file sets the URL and quiets the logs. Records like <code>Order</code> are a compact way to model data; if they&#8217;re new, compare them with a <a href="https://aspdotnethelp.com/structure-in-csharp/">structure in C#</a>.</p>



<pre class="wp-block-code"><code>{
  &quot;Logging&quot;: {
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Warning&quot;,
      &quot;Microsoft.Hosting.Lifetime&quot;: &quot;Information&quot;
    }
  },
  &quot;AllowedHosts&quot;: &quot;*&quot;,
  &quot;Urls&quot;: &quot;http://localhost:5280&quot;
}</code></pre>



<p class="wp-block-paragraph">I keep a <code>UsStoreOrders.http</code> file for quick manual calls from the editor:</p>



<pre class="wp-block-code"><code>@host = http://localhost:5280

GET {{host}}/about
Accept: application/json

###

GET {{host}}/orders/1001
Accept: application/json</code></pre>



<p class="wp-block-paragraph">I started the API with <code>dotnet run --project UsStoreOrders -- --environment Development</code> in one terminal and called it from another:</p>



<pre class="wp-block-code"><code>dotnet --version
curl -s -w &quot;\n&quot; http://localhost:5280/about
curl -s -w &quot;\n&quot; http://localhost:5280/orders/1001
curl -s -o NUL -w &quot;swagger.json: %{http_code}\n&quot; http://localhost:5280/swagger/v1/swagger.json</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>8.0.427
{&quot;runtime&quot;:&quot;.NET 8.0.33&quot;,&quot;target&quot;:&quot;.NETCoreApp,Version=v8.0&quot;}
{&quot;id&quot;:1001,&quot;customer&quot;:&quot;Jenna Moore&quot;,&quot;city&quot;:&quot;Austin&quot;,&quot;state&quot;:&quot;TX&quot;,&quot;total&quot;:249.99}
swagger.json: 200</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-dotnet-8-app-before-upgrade-framework-description-1.png" alt="VS Code showing the .NET 8 Program.cs with Swashbuckle and the terminal output .NET 8.0.33 from the /about endpoint" class="wp-image-2387" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-dotnet-8-app-before-upgrade-framework-description-1.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-dotnet-8-app-before-upgrade-framework-description-1-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-dotnet-8-app-before-upgrade-framework-description-1-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-dotnet-8-app-before-upgrade-framework-description-1-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">Before the upgrade: SDK 8.0.427, runtime .NET 8.0.33, Swagger returns 200</figcaption></figure>
</div>


<p class="wp-block-paragraph">Everything works: the SDK is 8.0.427, the runtime is .NET 8.0.33, and Swagger answers. You can browse more Minimal API projects like this one in my <a href="https://aspdotnethelp.com/asp-net-core-tutorials/">ASP.NET Core tutorials</a>.</p>



<h2 class="wp-block-heading">Step 2: Install the .NET 10 SDK and Update global.json</h2>



<p class="wp-block-paragraph">Download the .NET 10 SDK from dotnet.microsoft.com, or use <code>winget install Microsoft.DotNet.SDK.10</code>. Then change the <code>version</code> in <code>global.json</code>. Microsoft&#8217;s migration guide shows this exact edit:</p>



<pre class="wp-block-code"><code>{
  &quot;sdk&quot;: {
    &quot;version&quot;: &quot;10.0.100&quot;,
    &quot;rollForward&quot;: &quot;latestFeature&quot;
  }
}</code></pre>



<p class="wp-block-paragraph">I used 10.0.100 with <code>latestFeature</code>, so any newer 10.0 SDK satisfies it. Check that the CLI picked it up:</p>



<pre class="wp-block-code"><code>dotnet --version</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>10.0.402</code></pre>



<p class="wp-block-paragraph">If you skip this step, the build fails because a .NET 8 SDK can&#8217;t target <code>net10.0</code>. Not sure if you even need to move? My post on <a href="https://aspdotnethelp.com/dotnet-8-9-end-of-support/">.NET 8 and .NET 9 end of support</a> explains what happens after November 10.</p>



<h2 class="wp-block-heading">Step 3: Change TargetFramework From net8.0 to net10.0</h2>



<p class="wp-block-paragraph">Next, edit the project file. This one-line change is what most people mean by upgrading to .NET 10:</p>



<pre class="wp-block-code"><code>&lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;</code></pre>



<p class="wp-block-paragraph">I changed only the target framework and left every package at 8.x, then built:</p>



<pre class="wp-block-code"><code>dotnet build UsStoreOrders</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>  Determining projects to restore...
  Restored C:\DotNetDemo\UpgradeDemo\UsStoreOrders\UsStoreOrders.csproj (in 348 ms).
  UsStoreOrders -&gt; C:\DotNetDemo\UpgradeDemo\UsStoreOrders\bin\Debug\net10.0\UsStoreOrders.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.21</code></pre>



<p class="wp-block-paragraph">No warnings, no errors. The 8.x packages still compile against .NET 10, which surprises people. But a clean build doesn&#8217;t mean the upgrade&#8217;s done, because you&#8217;re still shipping .NET 8 libraries that won&#8217;t get .NET 10 fixes.</p>



<p class="wp-block-paragraph">With several projects, change all of them together, or move the property into a shared <code>Directory.Build.props</code>. Tracking each project&#8217;s settings is easier when you know your <a href="https://aspdotnethelp.com/namespace-in-csharp-dot-net/">namespaces and project structure</a> well.</p>



<h2 class="wp-block-heading">Step 4: Update NuGet Packages to the .NET 10 Versions</h2>



<p class="wp-block-paragraph">Microsoft&#8217;s guidance is to update every <code>Microsoft.AspNetCore.*</code>, <code>Microsoft.EntityFrameworkCore.*</code>, <code>Microsoft.Extensions.*</code> and <code>System.Net.Http.Json</code> reference to 10.0.0 or later. To see what&#8217;s behind, run:</p>



<pre class="wp-block-code"><code>dotnet list UsStoreOrders package --outdated</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>  Determining projects to restore...
  All projects are up-to-date for restore.

The following sources were used:
   https://api.nuget.org/v3/index.json
   C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\

Project `UsStoreOrders` has the following updates to its packages
   [net10.0]: 
   Top-level Package                             Requested   Resolved   Latest 
   &gt; Microsoft.AspNetCore.OpenApi                8.0.11      8.0.11     10.0.14
   &gt; Microsoft.EntityFrameworkCore.InMemory      8.0.11      8.0.11     10.0.14
   &gt; Swashbuckle.AspNetCore                      6.6.2       6.6.2      10.3.1</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-list-package-outdated-net10-upgrade.png" alt="VS Code with UsStoreOrders.csproj open and dotnet list package --outdated showing the 10.0 versions of Microsoft.AspNetCore.OpenApi and EF Core InMemory" class="wp-image-2077" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-list-package-outdated-net10-upgrade.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-list-package-outdated-net10-upgrade-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-list-package-outdated-net10-upgrade-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-list-package-outdated-net10-upgrade-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">dotnet list package &#8211;outdated lists the latest version of each package</figcaption></figure>
</div>


<p class="wp-block-paragraph">I updated the two Microsoft packages with <code>dotnet add package</code>, which rewrites the version in the project file:</p>



<pre class="wp-block-code"><code>dotnet add UsStoreOrders package Microsoft.AspNetCore.OpenApi --version 10.0.14
dotnet add UsStoreOrders package Microsoft.EntityFrameworkCore.InMemory --version 10.0.14</code></pre>



<p class="wp-block-paragraph">I left Swashbuckle at 6.6.2 on purpose. Many teams update only the Microsoft packages first, and I wanted to see what happens. Here&#8217;s the project file at that point:</p>



<pre class="wp-block-code"><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk.Web&quot;&gt;

  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
  &lt;/PropertyGroup&gt;

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.OpenApi&quot; Version=&quot;10.0.14&quot; /&gt;
    &lt;PackageReference Include=&quot;Microsoft.EntityFrameworkCore.InMemory&quot; Version=&quot;10.0.14&quot; /&gt;
    &lt;PackageReference Include=&quot;Swashbuckle.AspNetCore&quot; Version=&quot;6.6.2&quot; /&gt;
  &lt;/ItemGroup&gt;

&lt;/Project&gt;</code></pre>



<h2 class="wp-block-heading">Step 5: Fix the .NET 10 Breaking Changes You Hit</h2>



<p class="wp-block-paragraph">This is where my upgrade got interesting. The build passed, but with warnings, and the app crashed before it served a single request:</p>



<pre class="wp-block-code"><code>dotnet run --project UsStoreOrders -- --environment Development</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>C:\DotNetDemo\UpgradeDemo\UsStoreOrders\Program.cs(26,1): warning ASPDEPR002: &#x27;OpenApiEndpointConventionBuilderExtensions.WithOpenApi&lt;TBuilder&gt;(TBuilder)&#x27; is obsolete: &#x27;WithOpenApi is deprecated and will be removed in a future release. For more information, visit https://aka.ms/aspnet/deprecate/002.&#x27; (https://aka.ms/aspnet/deprecate/002) [C:\DotNetDemo\UpgradeDemo\UsStoreOrders\UsStoreOrders.csproj]
C:\DotNetDemo\UpgradeDemo\UsStoreOrders\Program.cs(32,1): warning ASPDEPR002: &#x27;OpenApiEndpointConventionBuilderExtensions.WithOpenApi&lt;TBuilder&gt;(TBuilder)&#x27; is obsolete: &#x27;WithOpenApi is deprecated and will be removed in a future release. For more information, visit https://aka.ms/aspnet/deprecate/002.&#x27; (https://aka.ms/aspnet/deprecate/002) [C:\DotNetDemo\UpgradeDemo\UsStoreOrders\UsStoreOrders.csproj]
C:\DotNetDemo\UpgradeDemo\UsStoreOrders\Program.cs(35,1): warning ASPDEPR002: &#x27;OpenApiEndpointConventionBuilderExtensions.WithOpenApi&lt;TBuilder&gt;(TBuilder)&#x27; is obsolete: &#x27;WithOpenApi is deprecated and will be removed in a future release. For more information, visit https://aka.ms/aspnet/deprecate/002.&#x27; (https://aka.ms/aspnet/deprecate/002) [C:\DotNetDemo\UpgradeDemo\UsStoreOrders\UsStoreOrders.csproj]
Unhandled exception. System.TypeLoadException: Method &#x27;GetSwagger&#x27; in type &#x27;Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator&#x27; from assembly &#x27;Swashbuckle.AspNetCore.SwaggerGen, Version=6.6.2.0, Culture=neutral, PublicKeyToken=d84d99fb0135530a&#x27; does not have an implementation.
   at Microsoft.Extensions.DependencyInjection.SwaggerGenServiceCollectionExtensions.AddSwaggerGen(IServiceCollection services, Action`1 setupAction)
   at Microsoft.Extensions.DependencyInjection.SwaggerGenServiceCollectionExtensions.AddSwaggerGen(IServiceCollection services, Action`1 setupAction)
   at Program.&lt;Main&gt;$(String[] args) in C:\DotNetDemo\UpgradeDemo\UsStoreOrders\Program.cs:line 6</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-aspdepr002-withopenapi-swashbuckle-typeloadexception-1.png" alt="VS Code terminal showing ASPDEPR002 WithOpenApi warnings and a System.TypeLoadException from Swashbuckle 6.6.2 after updating Microsoft.AspNetCore.OpenApi to 10.0" class="wp-image-2388" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-aspdepr002-withopenapi-swashbuckle-typeloadexception-1.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-aspdepr002-withopenapi-swashbuckle-typeloadexception-1-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-aspdepr002-withopenapi-swashbuckle-typeloadexception-1-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-aspdepr002-withopenapi-swashbuckle-typeloadexception-1-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">Two problems after the package update: ASPDEPR002 warnings and a startup crash in AddSwaggerGen</figcaption></figure>
</div>


<p class="wp-block-paragraph">Exceptions at startup like this one can&#8217;t be handled with a <a href="https://aspdotnethelp.com/try-catch-in-csharp/">try-catch block in C#</a>, because the type itself fails to load. You fix them by fixing the package graph.</p>



<h3 class="wp-block-heading">Fix 1: Replace Swashbuckle 6.x With AddOpenApi and MapOpenApi</h3>



<p class="wp-block-paragraph"><code>Microsoft.AspNetCore.OpenApi</code> 10.0 brings in a newer <code>Microsoft.OpenApi</code> library than Swashbuckle 6.6.2 was built for. The <code>TypeLoadException</code> on <code>GetSwagger</code> is that mismatch. You have two ways out:</p>



<ul class="wp-block-list">
<li>Upgrade Swashbuckle too. <code>dotnet list package --outdated</code> showed 10.3.1; I tried it in a copy of the project, and it started and served <code>swagger.json</code> on .NET 10.</li>

<li>Drop Swashbuckle and use the built-in generator. The ASP.NET Core Web API template now calls <code>AddOpenApi()</code> and <code>MapOpenApi()</code> instead.</li>
</ul>



<p class="wp-block-paragraph">I went with the built-in option, since it&#8217;s one package fewer to keep in sync. I removed the package from the CLI:</p>



<pre class="wp-block-code"><code>dotnet remove UsStoreOrders package Swashbuckle.AspNetCore</code></pre>



<h3 class="wp-block-heading">Fix 2: Remove WithOpenApi to Clear ASPDEPR002</h3>



<p class="wp-block-paragraph">Microsoft deprecated <code>WithOpenApi</code> in .NET 10. It still compiles and runs, but every call raises warning ASPDEPR002:</p>



<pre class="wp-block-code"><code>C:\DotNetDemo\UpgradeDemo\UsStoreOrders\Program.cs(26,1): warning ASPDEPR002: &#x27;OpenApiEndpointConventionBuilderExtensions.WithOpenApi&lt;TBuilder&gt;(TBuilder)&#x27; is obsolete: &#x27;WithOpenApi is deprecated and will be removed in a future release. For more information, visit https://aka.ms/aspnet/deprecate/002.&#x27; (https://aka.ms/aspnet/deprecate/002) [C:\DotNetDemo\UpgradeDemo\UsStoreOrders\UsStoreOrders.csproj]</code></pre>



<p class="wp-block-paragraph">The fix is to delete the <code>.WithOpenApi()</code> calls. If you used the overload to tweak an operation, move that code into <code>AddOpenApiOperationTransformer</code>. Swashbuckle users would use an <code>IOperationFilter</code>, a plain <a href="https://aspdotnethelp.com/interface-in-csharp/">interface in C#</a> you implement.</p>



<p class="wp-block-paragraph">Here&#8217;s the finished .NET 10 <code>Program.cs</code>:</p>



<pre class="wp-block-code"><code>using System.Runtime.InteropServices;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddDbContext&lt;StoreDb&gt;(o =&gt; o.UseInMemoryDatabase(&quot;orders&quot;));
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService&lt;StoreDb&gt;();
    db.Orders.AddRange(
        new Order(1001, &quot;Jenna Moore&quot;, &quot;Austin&quot;, &quot;TX&quot;, 249.99m),
        new Order(1002, &quot;Ryan Carter&quot;, &quot;Denver&quot;, &quot;CO&quot;, 89.50m),
        new Order(1003, &quot;Chloe Brady&quot;, &quot;Seattle&quot;, &quot;WA&quot;, 1325.00m));
    db.SaveChanges();
}

app.MapGet(&quot;/about&quot;, () =&gt; new
{
    Runtime = RuntimeInformation.FrameworkDescription,
    Target = AppContext.TargetFrameworkName
}).WithName(&quot;About&quot;);

app.MapGet(&quot;/orders&quot;, (StoreDb db) =&gt; db.Orders.OrderBy(o =&gt; o.Id).ToListAsync())
   .WithName(&quot;ListOrders&quot;);

app.MapGet(&quot;/orders/{id:int}&quot;, async (int id, StoreDb db) =&gt;
    await db.Orders.FindAsync(id) is Order o ? Results.Ok(o) : Results.NotFound())
   .WithName(&quot;GetOrder&quot;);

app.Run();

public record Order(int Id, string Customer, string City, string State, decimal Total);

public class StoreDb(DbContextOptions&lt;StoreDb&gt; options) : DbContext(options)
{
    public DbSet&lt;Order&gt; Orders =&gt; Set&lt;Order&gt;();
}</code></pre>



<p class="wp-block-paragraph">And the final project file, two packages lighter on worries:</p>



<pre class="wp-block-code"><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk.Web&quot;&gt;

  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
  &lt;/PropertyGroup&gt;

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.OpenApi&quot; Version=&quot;10.0.14&quot; /&gt;
    &lt;PackageReference Include=&quot;Microsoft.EntityFrameworkCore.InMemory&quot; Version=&quot;10.0.14&quot; /&gt;
  &lt;/ItemGroup&gt;

&lt;/Project&gt;</code></pre>



<p class="wp-block-paragraph">The build is clean again:</p>



<pre class="wp-block-code"><code>  UsStoreOrders -&gt; C:\DotNetDemo\UpgradeDemo\UsStoreOrders\bin\Debug\net10.0\UsStoreOrders.dll
Build succeeded.
    0 Warning(s)
    0 Error(s)</code></pre>



<h2 class="wp-block-heading">Other Breaking Changes in .NET 9 and .NET 10 to Check</h2>



<p class="wp-block-paragraph">Jumping from .NET 8 to .NET 10 means you take the .NET 9 changes too. My demo didn&#8217;t hit these, but they&#8217;re on Microsoft&#8217;s breaking-change lists, and real apps do:</p>



<figure class="wp-block-table"><table><thead><tr><th>Change</th><th>Who it affects</th></tr></thead><tbody><tr><td>Cookie login redirects disabled for known API endpoints</td><td>APIs that relied on a redirect to a login page; they now get 401/403</td></tr><tr><td><code>WebHostBuilder</code>, <code>IWebHost</code> and <code>WebHost</code> are obsolete</td><td>Old <code>Startup.cs</code> hosting code; move to <code>WebApplication.CreateBuilder</code></td></tr><tr><td><code>IActionContextAccessor</code> is obsolete</td><td>MVC apps that inject it; use <code>IHttpContextAccessor</code> and endpoint metadata</td></tr><tr><td>Razor runtime compilation is obsolete</td><td>MVC/Razor Pages apps that edit views without rebuilding</td></tr><tr><td><code>dotnet restore</code> audits transitive packages</td><td>Builds that treat warnings as errors may now fail on NU190x audit warnings</td></tr><tr><td><code>UseStaticFiles</code> to <code>MapStaticAssets</code> (.NET 9)</td><td>MVC, Razor Pages and Blazor apps with static files (optional but recommended)</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">If you serialize your own JSON, also test payloads carefully; I covered serializer quirks in <a href="https://aspdotnethelp.com/convert-object-to-json-without-escape-characters-in-csharp/">converting an object to JSON without escape characters</a>.</p>



<h2 class="wp-block-heading">Step 6: Run the Upgraded App on .NET 10</h2>



<p class="wp-block-paragraph">Same commands as Step 1, same requests, new runtime. The only difference in the output is the OpenAPI URL, since the built-in document lives at <code>/openapi/v1.json</code>:</p>



<pre class="wp-block-code"><code>dotnet --version
curl -s -w &quot;\n&quot; http://localhost:5280/about
curl -s -w &quot;\n&quot; http://localhost:5280/orders/1001
curl -s -o NUL -w &quot;openapi/v1.json: %{http_code}\n&quot; http://localhost:5280/openapi/v1.json</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>10.0.402
{&quot;runtime&quot;:&quot;.NET 10.0.13&quot;,&quot;target&quot;:&quot;.NETCoreApp,Version=v10.0&quot;}
{&quot;id&quot;:1001,&quot;customer&quot;:&quot;Jenna Moore&quot;,&quot;city&quot;:&quot;Austin&quot;,&quot;state&quot;:&quot;TX&quot;,&quot;total&quot;:249.99}
openapi/v1.json: 200</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-app-running-on-dotnet-10-after-upgrade-1.png" alt="VS Code with the .NET 10 Program.cs using AddOpenApi and MapOpenApi, terminal showing .NET 10.0.13 and .NETCoreApp,Version=v10.0 from the /about endpoint" class="wp-image-2386" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-app-running-on-dotnet-10-after-upgrade-1.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-app-running-on-dotnet-10-after-upgrade-1-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-app-running-on-dotnet-10-after-upgrade-1-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-app-running-on-dotnet-10-after-upgrade-1-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">After the upgrade: SDK 10.0.402, runtime .NET 10.0.13, OpenAPI document returns 200</figcaption></figure>
</div>


<p class="wp-block-paragraph"><code>FrameworkDescription</code> went from .NET 8.0.33 to .NET 10.0.13, and the order data is identical. The built-in document also reports <code>"openapi": "3.1.1"</code>, since .NET 10 generates OpenAPI 3.1 by default. I list other changes in <a href="https://aspdotnethelp.com/whats-new-in-dotnet-10/">what is new in .NET 10</a>.</p>



<p class="wp-block-paragraph">The project also compiles as C# 14 now. Nothing in my code needed it, but features like the <code>field</code> keyword are free to use; see <a href="https://aspdotnethelp.com/whats-new-in-csharp-14/">the new features in C# 14</a>.</p>



<h2 class="wp-block-heading">How Do You Update Docker Images and CI for .NET 10?</h2>



<p class="wp-block-paragraph">Your code isn&#8217;t the only place that names .NET 8. Search the repo for <code>8.0</code> and update Dockerfiles and pipelines. Here&#8217;s a Dockerfile snippet for this project with the .NET 10 tags (I didn&#8217;t build the image on this PC):</p>



<pre class="wp-block-code"><code>FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish UsStoreOrders/UsStoreOrders.csproj -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT [&quot;dotnet&quot;, &quot;UsStoreOrders.dll&quot;]</code></pre>



<p class="wp-block-paragraph">Watch the base OS. The default .NET 10 tags use Ubuntu 24.04 &#8220;Noble&#8221; instead of Debian, and Microsoft doesn&#8217;t ship Debian images for .NET 10. If your Dockerfile runs <code>apt-get</code>, test it again.</p>



<p class="wp-block-paragraph">For GitHub Actions, bump the SDK in <code>actions/setup-dotnet</code>. Pointing it at <code>global.json</code> keeps CI and your machine on the same SDK:</p>



<pre class="wp-block-code"><code>- uses: actions/setup-dotnet@v4
  with:
    dotnet-version: &#x27;10.0.x&#x27;
    # or: global-json-file: global.json</code></pre>



<p class="wp-block-paragraph">Azure DevOps works the same way with the <code>UseDotNet@2</code> task. If you deploy to Azure, my <a href="https://aspdotnethelp.com/ai-azure-dotnet-tutorials/">AI and Azure .NET tutorials</a> cover the hosting side.</p>



<h2 class="wp-block-heading">Can a Tool Upgrade .NET 8 to .NET 10 for You?</h2>



<p class="wp-block-paragraph">Partly. The .NET Upgrade Assistant is officially deprecated now. Microsoft points you to the GitHub Copilot modernization chat agent instead, which comes with Visual Studio 2026 and Visual Studio 2022 17.14.16 or later.</p>



<p class="wp-block-paragraph">The agent analyzes your projects and dependencies, builds a step-by-step plan, and commits each change so you can roll back. For a project this size, I&#8217;d still do it by hand; it&#8217;s five edits, and you learn where your risks are.</p>



<p class="wp-block-paragraph">On big solutions with dozens of projects, the agent saves real time. Just review every commit, the same way you&#8217;d review a teammate&#8217;s pull request. Upgrade questions like this also come up in <a href="https://aspdotnethelp.com/dot-net-core-interview-questions-and-answers/">.NET Core interview questions</a>.</p>



<h2 class="wp-block-heading">My .NET 8 to .NET 10 Upgrade Checklist</h2>



<p class="wp-block-paragraph">Here&#8217;s the order I&#8217;d follow on any ASP.NET Core 8 app:</p>



<ol class="wp-block-list">
<li>Build and run on .NET 8, and save sample responses.</li>

<li>Install the .NET 10 SDK and update <code>global.json</code>.</li>

<li>Change <code>net8.0</code> to <code>net10.0</code> in every project.</li>

<li>Run <code>dotnet list package --outdated</code> and update Microsoft packages to 10.0.x.</li>

<li>Update or replace third-party packages that depend on old Microsoft libraries, like Swashbuckle 6.x.</li>

<li>Fix warnings such as ASPDEPR002, then run the app and your tests.</li>

<li>Update Dockerfiles, CI and hosting to .NET 10.</li>
</ol>



<p class="wp-block-paragraph">Compare the responses from step 1 with the new ones before you deploy. All the .NET release posts live on my <a href="https://aspdotnethelp.com/dotnet-versions/">.NET versions hub</a>.</p>



<p class="wp-block-paragraph">If you&#8217;re still getting comfortable with the language side of .NET, these are worth a read:</p>



<ul class="wp-block-list">
<li><a href="https://aspdotnethelp.com/create-a-console-application-in-visual-studio-code/">Create a console app in VS Code</a></li>


<li><a href="https://aspdotnethelp.com/what-is-csharp/">What C# is and what it&#8217;s used for</a></li>

<li><a href="https://aspdotnethelp.com/csharp-interview-questions-and-answers-for-experienced-professionals/">C# interview questions for experienced developers</a></li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-up0000001" class="rank-math-list-item">
<h3 class="rank-math-question ">Is upgrading from .NET 8 to .NET 10 a breaking change?</h3>
<div class="rank-math-answer ">

<p>Usually it&#8217;s small. Changing <code>net8.0</code> to <code>net10.0</code> often builds cleanly, but package updates can surface obsolete APIs (like <code>WithOpenApi</code>) and version conflicts (like Swashbuckle 6.x). Test the app, not just the build.</p>

</div>
</div>
<div id="faq-question-up0000002" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I skip .NET 9 and go straight from .NET 8 to .NET 10?</h3>
<div class="rank-math-answer ">

<p>Yes. You don&#8217;t install or target .NET 9 in between. You do need to review the .NET 9 breaking changes as well as the .NET 10 ones, since you&#8217;re crossing both releases.</p>

</div>
</div>
<div id="faq-question-up0000003" class="rank-math-list-item">
<h3 class="rank-math-question ">Do I have to update NuGet packages when I upgrade to .NET 10?</h3>
<div class="rank-math-answer ">

<p>You should. Microsoft recommends updating <code>Microsoft.AspNetCore.*</code>, <code>Microsoft.EntityFrameworkCore.*</code>, <code>Microsoft.Extensions.*</code> and <code>System.Net.Http.Json</code> to 10.0.0 or later. The steps are in <a href="https://learn.microsoft.com/en-us/aspnet/core/migration/90-to-100" target="_blank" rel="noopener">Microsoft&#8217;s .NET 9 to .NET 10 migration guide</a>.</p>

</div>
</div>
<div id="faq-question-up0000004" class="rank-math-list-item">
<h3 class="rank-math-question ">What does warning ASPDEPR002 mean?</h3>
<div class="rank-math-answer ">

<p>It means you&#8217;re calling <code>WithOpenApi</code>, which is deprecated in .NET 10. Remove the call, or move per-endpoint changes to <code>AddOpenApiOperationTransformer</code>. Microsoft lists it in <a href="https://learn.microsoft.com/en-us/aspnet/core/breaking-changes/10/overview" target="_blank" rel="noopener">Breaking changes in ASP.NET Core 10</a>.</p>

</div>
</div>
<div id="faq-question-up0000005" class="rank-math-list-item">
<h3 class="rank-math-question ">Does Swashbuckle work with .NET 10?</h3>
<div class="rank-math-answer ">

<p>Version 6.6.2 crashed with a <code>TypeLoadException</code> once Microsoft.AspNetCore.OpenApi 10.0 was in the project. Swashbuckle 10.3.1 worked in my test. Or switch to the built-in <code>AddOpenApi</code> and <code>MapOpenApi</code>.</p>

</div>
</div>
<div id="faq-question-up0000006" class="rank-math-list-item">
<h3 class="rank-math-question ">When does .NET 8 support end?</h3>
<div class="rank-math-answer ">

<p>November 10, 2026, the same day as .NET 9. .NET 10 is an LTS release supported until November 14, 2028.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img alt='Bijay Kumar' src='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=100&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=200&#038;d=mm&#038;r=g 2x' class='avatar avatar-100 photo' height='100' width='100' itemprop="image"/></div><div class="saboxplugin-authorname"><a href="https://aspdotnethelp.com/bijay-kumar/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Bijay Kumar is a renowned software engineer, accomplished author, and distinguished <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000972" rel="nofollow noopener" target="_blank">Microsoft Most Valuable Professional</a> (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. <a href="https://aspdotnethelp.com/about/" rel="nofollow noopener">Read more&#8230;</a></p>
</div></div><div class="saboxplugin-web "><a href="https://aspdotnethelp.com" target="_self">aspdotnethelp.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials "><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-linkedin" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 448 512"><path fill="currentColor" d="M100.3 480H7.4V180.9h92.9V480zM53.8 140.1C24.1 140.1 0 115.5 0 85.8 0 56.1 24.1 32 53.8 32c29.7 0 53.8 24.1 53.8 53.8 0 29.7-24.1 54.3-53.8 54.3zM448 480h-92.7V334.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V480h-92.8V180.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V480z"></path></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 264 512"><path fill="currentColor" d="M76.7 512V283H0v-91h76.7v-71.7C76.7 42.4 124.3 0 193.8 0c33.3 0 61.9 2.5 70.2 3.6V85h-48.2c-37.8 0-45.1 18-45.1 44.3V192H256l-11.7 91h-73.6v229"></path></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/c/enjoysharepoint?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 30 30"><path d="M26.37,26l-8.795-12.822l0.015,0.012L25.52,4h-2.65l-6.46,7.48L11.28,4H4.33l8.211,11.971L12.54,15.97L3.88,26h2.65 l7.182-8.322L19.42,26H26.37z M10.23,6l12.34,18h-2.1L8.12,6H10.23z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>What&#8217;s New in .NET 10: 8 Features C# and ASP.NET Core Developers Should Use</title>
		<link>https://aspdotnethelp.com/whats-new-in-dotnet-10/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 26 Oct 2026 17:46:54 +0000</pubDate>
				<category><![CDATA[ASP.NET Core]]></category>
		<guid isPermaLink="false">https://aspdotnethelp.com/?p=2045</guid>

					<description><![CDATA[.NET 10 came out on November 11, 2025, and it&#8217;s a long-term support release. That alone makes it the version I&#8217;d pick for any new C# or ASP.NET Core project in 2026. So, what&#8217;s new in .NET 10? You can run a single .cs file with dotnet run app.cs, write C# 14, validate Minimal API ... <a title="What&#8217;s New in .NET 10: 8 Features C# and ASP.NET Core Developers Should Use" class="read-more" href="https://aspdotnethelp.com/whats-new-in-dotnet-10/" aria-label="Read more about What&#8217;s New in .NET 10: 8 Features C# and ASP.NET Core Developers Should Use">Read more &#62;&#62;</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">.NET 10 came out on November 11, 2025, and it&#8217;s a long-term support release. That alone makes it the version I&#8217;d pick for any new C# or ASP.NET Core project in 2026.</p>



<p class="wp-block-paragraph">So, what&#8217;s new in .NET 10? You can run a single <code>.cs</code> file with <code>dotnet run app.cs</code>, write C# 14, validate Minimal API input with one line, stream server-sent events, and serve OpenAPI 3.1 as YAML.</p>



<p class="wp-block-paragraph">In this tutorial, I&#8217;ll test <strong>what&#8217;s new in .NET 10</strong> feature by feature. Each section has a small program, the real output, and a short &#8220;How does this code work?&#8221; walk-through, so you can judge what&#8217;s worth adopting.</p>



<p class="wp-block-paragraph">My setup is Windows 11, VS Code, and .NET SDK 10.0.402 (runtime 10.0.13). Most demos are single-file apps, so you can copy one file into an empty folder and run it.</p>



<h2 class="wp-block-heading">.NET 10 at a Glance: Release Date, LTS Support and Key Features</h2>



<p class="wp-block-paragraph">Here&#8217;s the short version before we get into code. I&#8217;ve grouped the changes a working C# or web developer will actually notice:</p>



<figure class="wp-block-table"><table><thead><tr><th>Area</th><th>What&#8217;s new in .NET 10</th><th>Why you&#8217;d care</th></tr></thead><tbody><tr><td>SDK</td><td>File-based apps, <code>dotnet tool exec</code>, <code>dnx</code>, noun-first CLI commands</td><td>Scripts and tools without a project file</td></tr><tr><td>C# 14</td><td><code>field</code> keyword, extension members, null-conditional assignment</td><td>Less boilerplate in everyday classes</td></tr><tr><td>ASP.NET Core 10</td><td>Minimal API validation, server-sent events, OpenAPI 3.1 + YAML, passkeys</td><td>Fewer NuGet packages and hand-written checks</td></tr><tr><td>Blazor</td><td><code>[PersistentState]</code>, WebAssembly preloading, <code>NotFound()</code></td><td>Faster loads and cleaner 404s</td></tr><tr><td>Libraries</td><td><code>JsonSerializerOptions.Strict</code>, numeric string ordering, new <code>OrderedDictionary</code> overloads</td><td>Safer JSON and fewer custom helpers</td></tr><tr><td>Runtime</td><td>Better JIT inlining, devirtualization, stack allocation, AVX10.2</td><td>Speed you get without code changes</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Support is the other half of the story. Microsoft supports an LTS release for three years, and two older versions retire on the same day this November (my <a href="https://aspdotnethelp.com/dotnet-versions/">.NET versions and support dates</a> page tracks every release):</p>



<figure class="wp-block-table"><table><thead><tr><th>Version</th><th>Type</th><th>Released</th><th>End of support</th></tr></thead><tbody><tr><td>.NET 10</td><td>LTS</td><td>November 11, 2025</td><td>November 14, 2028</td></tr><tr><td>.NET 9</td><td>STS</td><td>November 12, 2024</td><td>November 10, 2026</td></tr><tr><td>.NET 8</td><td>LTS</td><td>November 14, 2023</td><td>November 10, 2026</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">As I write this on October 26, 2026, that&#8217;s just 15 days away. My guide to <a href="https://aspdotnethelp.com/dotnet-8-9-end-of-support/">.NET 8 and .NET 9 end of support</a> covers what that date means for your apps, and you can <a href="https://aspdotnethelp.com/check-dotnet-version/">check which .NET version is installed</a> before you start.</p>



<h2 class="wp-block-heading">Feature 1: File-Based Apps Run a Single .cs File in .NET 10</h2>



<p class="wp-block-paragraph">This is the change I use most. A .NET 10 SDK can build and run one C# file with no <code>.csproj</code>, no <code>Program</code> class, and no solution. Directives at the top of the file replace the project settings.</p>



<p class="wp-block-paragraph">Here&#8217;s a small script that pulls in a NuGet package with <code>#:package</code> and prints where it&#8217;s running from:</p>



<pre class="wp-block-code"><code>#:package Humanizer@3.0.10
using Humanizer;

Console.WriteLine(&quot;order_shipped_to_dallas&quot;.Humanize());
Console.WriteLine(TimeSpan.FromMinutes(95).Humanize(2));
Console.WriteLine(DateTime.UtcNow.AddHours(-3).Humanize());
Console.WriteLine($&quot;Running {AppContext.GetData(&quot;EntryPointFilePath&quot;)}&quot;);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>order shipped to dallas
1 hour, 35 minutes
3 hours ago
Running C:\DotNetDemo\Net10Demo\Hello.cs</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-file-based-app-dotnet-run-hello-cs.png" alt="VS Code running dotnet run Hello.cs, a .NET 10 file-based app that uses the #:package directive to load Humanizer" class="wp-image-2059" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-file-based-app-dotnet-run-hello-cs.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-file-based-app-dotnet-run-hello-cs-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-file-based-app-dotnet-run-hello-cs-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-file-based-app-dotnet-run-hello-cs-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">dotnet run Hello.cs restores Humanizer and runs the file with no project</figcaption></figure>
</div>


<h3 class="wp-block-heading">How does this code work?</h3>



<ul class="wp-block-list">
<li><code>#:package Humanizer@3.0.10</code> adds a NuGet reference, exactly like a <code>PackageReference</code> in a project. Other directives are <code>#:sdk</code>, <code>#:property</code> and <code>#:project</code>.</li>

<li><code>dotnet run Hello.cs</code> creates a hidden virtual project, restores, builds and runs it. The build output goes to a temp folder, not next to your file.</li>

<li><code>AppContext.GetData("EntryPointFilePath")</code> returns the path of the running file, which is handy for scripts that read files beside them.</li>
</ul>



<p class="wp-block-paragraph">One gotcha caught me right away. File-based apps target native AOT by default, so reflection-based <code>System.Text.Json</code> is switched off. My first JSON demo crashed with this message:</p>



<pre class="wp-block-code"><code>Unhandled exception. System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the &#x27;JsonSerializerOptions.TypeInfoResolver&#x27; property.</code></pre>



<p class="wp-block-paragraph">The fix is one line at the top of the file: <code>#:property PublishAot=false</code>. You&#8217;ll see it in the web and JSON demos below. Keep AOT on when you plan to <code>dotnet publish app.cs</code> to a small native executable.</p>



<p class="wp-block-paragraph">When a script grows up, <code>dotnet project convert Hello.cs</code> turns it into a normal project. It created a <code>Hello</code> folder with a copy of the code (directives removed) and this project file:</p>



<pre class="wp-block-code"><code>﻿&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;

  &lt;PropertyGroup&gt;
    &lt;OutputType&gt;Exe&lt;/OutputType&gt;
    &lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;PublishAot&gt;true&lt;/PublishAot&gt;
    &lt;PackAsTool&gt;true&lt;/PackAsTool&gt;
    &lt;UserSecretsId&gt;Hello-580d788577a6cb5a279acf621b27d3108617e2bb7a00b143197ab430d3b18907&lt;/UserSecretsId&gt;
  &lt;/PropertyGroup&gt;

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Humanizer&quot; Version=&quot;3.0.10&quot; /&gt;
  &lt;/ItemGroup&gt;

&lt;/Project&gt;</code></pre>



<p class="wp-block-paragraph">From there it&#8217;s a regular console project, the same kind I set up in my guide to <a href="https://aspdotnethelp.com/create-a-console-application-in-visual-studio-code/">create a console application in VS Code</a>.</p>



<h2 class="wp-block-heading">Feature 2: C# 14 Comes With .NET 10</h2>



<p class="wp-block-paragraph">Every .NET 10 project compiles as C# 14 by default. Three features show up in my code almost every day: the <code>field</code> keyword, extension members, and null-conditional assignment.</p>



<pre class="wp-block-code"><code>var order = new Order { Customer = &quot;  Chloe Brady  &quot; };
Console.WriteLine($&quot;[{order.Customer}]&quot;);

Order? missing = null;
missing?.Customer = &quot;Nobody&quot;;        // null-conditional assignment
Console.WriteLine($&quot;missing is still {missing?.ToString() ?? &quot;null&quot;}&quot;);

decimal total = 1249.5m;
Console.WriteLine(total.AsUsd);       // extension property

class Order
{
    public string Customer { get; set =&gt; field = value.Trim(); } = &quot;&quot;;
}

static class MoneyExtensions
{
    extension(decimal amount)
    {
        public string AsUsd =&gt; amount.ToString(&quot;C&quot;, new System.Globalization.CultureInfo(&quot;en-US&quot;));
    }
}</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>[Chloe Brady]
missing is still null
$1,249.50</code></pre>



<h3 class="wp-block-heading">How does this code work?</h3>



<p class="wp-block-paragraph"><code>set =&gt; field = value.Trim();</code> uses the compiler&#8217;s backing field, so you can add logic to one accessor without declaring a private field.</p>



<p class="wp-block-paragraph"><code>missing?.Customer = "Nobody";</code> only assigns when <code>missing</code> isn&#8217;t null. Before C# 14, you&#8217;d write an <code>if</code> check first.</p>



<p class="wp-block-paragraph">The <code>extension(decimal amount)</code> block adds an <code>AsUsd</code> property to <code>decimal</code>. Older extension methods couldn&#8217;t add properties at all.</p>



<p class="wp-block-paragraph">There&#8217;s more: implicit span conversions, <code>nameof(List&lt;&gt;)</code>, partial constructors and user-defined compound operators. I walk through each one in <a href="https://aspdotnethelp.com/whats-new-in-csharp-14/">what is new in C# 14</a>, and the null-conditional change pairs well with these <a href="https://aspdotnethelp.com/how-to-check-for-null-values-in-c-sharp/">ways to check for null in C#</a>.</p>



<h2 class="wp-block-heading">Feature 3: Built-In Minimal API Validation in ASP.NET Core 10</h2>



<p class="wp-block-paragraph">Until .NET 10, Minimal APIs ignored <code>[Required]</code> and <code>[Range]</code>, so you wrote checks by hand or added a library (more web examples live in my <a href="https://aspdotnethelp.com/asp-net-core-tutorials/">ASP.NET Core tutorials</a>). Now one call, <code>builder.Services.AddValidation()</code>, validates query strings, headers and request bodies.</p>



<p class="wp-block-paragraph">This file-based web app starts Kestrel, posts a good order and a bad one to itself, prints both responses, and stops:</p>



<pre class="wp-block-code"><code>#:sdk Microsoft.NET.Sdk.Web
#:property PublishAot=false
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder();
builder.Logging.ClearProviders();
builder.Services.AddValidation();
var app = builder.Build();
app.MapPost(&quot;/orders&quot;, (Order o) =&gt; TypedResults.Created($&quot;/orders/{o.Id}&quot;, o));
app.Urls.Add(&quot;http://localhost:5187&quot;);
await app.StartAsync();

using var http = new HttpClient { BaseAddress = new(&quot;http://localhost:5187&quot;) };
string[] bodies = [&quot;&quot;&quot;{&quot;id&quot;:1,&quot;customer&quot;:&quot;Jenna Moore&quot;,&quot;state&quot;:&quot;TX&quot;,&quot;quantity&quot;:3}&quot;&quot;&quot;,
                   &quot;&quot;&quot;{&quot;id&quot;:2,&quot;customer&quot;:&quot;&quot;,&quot;state&quot;:&quot;Texas&quot;,&quot;quantity&quot;:0}&quot;&quot;&quot;];
foreach (var body in bodies)
{
    var res = await http.PostAsync(&quot;/orders&quot;, new StringContent(body, null, &quot;application/json&quot;));
    Console.WriteLine($&quot;{(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}&quot;);
}
await app.StopAsync();

public record Order(int Id, [Required] string Customer,
    [StringLength(2, MinimumLength = 2)] string State, [Range(1, 100)] int Quantity);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>201 {&quot;id&quot;:1,&quot;customer&quot;:&quot;Jenna Moore&quot;,&quot;state&quot;:&quot;TX&quot;,&quot;quantity&quot;:3}
400 {&quot;title&quot;:&quot;One or more validation errors occurred.&quot;,&quot;errors&quot;:{&quot;Customer&quot;:[&quot;The Customer field is required.&quot;],&quot;State&quot;:[&quot;The field State must be a string with a minimum length of 2 and a maximum length of 2.&quot;],&quot;Quantity&quot;:[&quot;The field Quantity must be between 1 and 100.&quot;]}}</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-minimal-api-validation-400-response-1.png" alt="VS Code running a .NET 10 Minimal API with AddValidation: a 201 Created response and a 400 response listing validation errors" class="wp-image-2378" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-minimal-api-validation-400-response-1.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-minimal-api-validation-400-response-1-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-minimal-api-validation-400-response-1-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-minimal-api-validation-400-response-1-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">The bad order gets a 400 with every failed rule, and the handler never runs</figcaption></figure>
</div>


<h3 class="wp-block-heading">How does this code work?</h3>



<ul class="wp-block-list">
<li><code>#:sdk Microsoft.NET.Sdk.Web</code> turns the single file into an ASP.NET Core app. That&#8217;s the same SDK a web <code>.csproj</code> uses.</li>

<li><code>AddValidation()</code> registers a source-generated validator for the <code>Order</code> record. It reads the DataAnnotations attributes on the record&#8217;s parameters.</li>

<li>The first order passes and returns <strong>201</strong>. The second fails three rules, so ASP.NET Core returns <strong>400</strong> with an <code>errors</code> object keyed by property name.</li>
</ul>



<p class="wp-block-paragraph">Need an endpoint to skip it? Add <code>.DisableValidation()</code> to that route. This topic comes up a lot in interviews now, so it&#8217;s worth adding to your <a href="https://aspdotnethelp.com/dot-net-core-interview-questions-and-answers/">.NET Core interview questions</a> prep.</p>



<h2 class="wp-block-heading">Feature 4: Server-Sent Events With TypedResults.ServerSentEvents</h2>



<p class="wp-block-paragraph">Server-sent events (SSE) push a one-way stream of updates over plain HTTP. They&#8217;re a lighter option than SignalR for dashboards, stock tickers or progress bars. ASP.NET Core 10 adds <code>TypedResults.ServerSentEvents</code>, so you just return an async stream:</p>



<pre class="wp-block-code"><code>#:sdk Microsoft.NET.Sdk.Web
#:property PublishAot=false

var builder = WebApplication.CreateBuilder();
builder.Logging.ClearProviders();
var app = builder.Build();
app.Urls.Add(&quot;http://localhost:5188&quot;);

app.MapGet(&quot;/prices&quot;, () =&gt; TypedResults.ServerSentEvents(Prices(), eventType: &quot;price&quot;));

await app.StartAsync();
using var http = new HttpClient { BaseAddress = new(&quot;http://localhost:5188&quot;) };
Console.Write(await http.GetStringAsync(&quot;/prices&quot;));
await app.StopAsync();

static async IAsyncEnumerable&lt;Quote&gt; Prices()
{
    foreach (var (symbol, usd) in new[] { (&quot;MSFT&quot;, 512.40m), (&quot;NVDA&quot;, 188.15m), (&quot;AAPL&quot;, 254.30m) })
    {
        await Task.Delay(200);
        yield return new Quote(symbol, usd);
    }
}

record Quote(string Symbol, decimal Usd);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>event: price
data: {&quot;symbol&quot;:&quot;MSFT&quot;,&quot;usd&quot;:512.40}

event: price
data: {&quot;symbol&quot;:&quot;NVDA&quot;,&quot;usd&quot;:188.15}

event: price
data: {&quot;symbol&quot;:&quot;AAPL&quot;,&quot;usd&quot;:254.30}</code></pre>



<h3 class="wp-block-heading">How does this code work?</h3>



<p class="wp-block-paragraph"><code>Prices()</code> is an <code>IAsyncEnumerable&lt;Quote&gt;</code> that yields three quotes, 200 ms apart.</p>



<p class="wp-block-paragraph"><code>TypedResults.ServerSentEvents(Prices(), eventType: "price")</code> writes each item as an <code>event:</code> line plus a JSON <code>data:</code> line, with a blank line between events.</p>



<p class="wp-block-paragraph">In a browser, you&#8217;d read the same stream with JavaScript&#8217;s <code>EventSource</code> and listen for the <code>price</code> event.</p>



<p class="wp-block-paragraph">The <code>foreach (var (symbol, usd) in ...)</code> loop deconstructs each pair, which I explain in my guide to <a href="https://aspdotnethelp.com/tuple-in-csharp/">tuples in C#</a>.</p>



<h2 class="wp-block-heading">Feature 5: OpenAPI 3.1 and YAML Documents in .NET 10</h2>



<p class="wp-block-paragraph">The built-in OpenAPI generator now writes OpenAPI 3.1 by default, and it can serve the document as YAML. You only change the route passed to <code>MapOpenApi</code>:</p>



<pre class="wp-block-code"><code>#:sdk Microsoft.NET.Sdk.Web
#:package Microsoft.AspNetCore.OpenApi@10.0.13
#:property PublishAot=false

var builder = WebApplication.CreateBuilder();
builder.Logging.ClearProviders();
builder.Services.AddOpenApi();
var app = builder.Build();
app.Urls.Add(&quot;http://localhost:5189&quot;);
app.MapOpenApi(&quot;/openapi/{documentName}.yaml&quot;);
app.MapGet(&quot;/stores/{state}&quot;, (string state, int? limit) =&gt; new[] { &quot;Austin&quot;, &quot;Dallas&quot; });

await app.StartAsync();
using var http = new HttpClient { BaseAddress = new(&quot;http://localhost:5189&quot;) };
Console.WriteLine(await http.GetStringAsync(&quot;/openapi/v1.yaml&quot;));
await app.StopAsync();</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>openapi: &#x27;3.1.1&#x27;
info:
  title: OpenApiYaml | v1
  version: 1.0.0
servers:
  - url: http://localhost:5189/
paths:
  &#x27;/stores/{state}&#x27;:
    get:
      tags:
        - OpenApiYaml
      parameters:
        - name: state
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            pattern: &#x27;^-?(?:0|[1-9]\d*)$&#x27;
            type:
              - integer
              - string
            format: int32
      responses:
        &#x27;200&#x27;:
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
tags:
  - name: OpenApiYaml</code></pre>



<h3 class="wp-block-heading">How does this code work?</h3>



<p class="wp-block-paragraph"><code>AddOpenApi()</code> plus <code>MapOpenApi("/openapi/{documentName}.yaml")</code> serves the document as YAML because the route ends in <code>.yaml</code>.</p>



<p class="wp-block-paragraph">The output starts with <code>openapi: '3.1.1'</code>. Older releases produced OpenAPI 3.0.</p>



<p class="wp-block-paragraph">The nullable <code>int? limit</code> shows up as a JSON Schema <code>type</code> array instead of the old <code>nullable: true</code> flag.</p>



<p class="wp-block-paragraph">With version 10.0.0 of the package, restore warned about a known vulnerability in <code>Microsoft.OpenApi</code> 2.0.0. Pinning the current 10.0.x patch made it go away. If nullable types are new to you, my post on <a href="https://aspdotnethelp.com/data-types-in-csharp/">C# data types</a> covers them.</p>



<h2 class="wp-block-heading">Feature 6: Stricter JSON in .NET 10 With JsonSerializerOptions.Strict</h2>



<p class="wp-block-paragraph">By default, <code>System.Text.Json</code> quietly takes the last value when a property appears twice. That&#8217;s a real security risk for payment or permission fields. .NET 10 adds <code>AllowDuplicateProperties</code> and a ready-made <code>JsonSerializerOptions.Strict</code> preset:</p>



<pre class="wp-block-code"><code>#:property PublishAot=false
using System.Text.Json;

string json = &quot;&quot;&quot;{ &quot;Customer&quot;: &quot;Ryan Carter&quot;, &quot;Total&quot;: 49.99, &quot;Total&quot;: 0.01 }&quot;&quot;&quot;;
var loose = JsonSerializer.Deserialize&lt;Invoice&gt;(json)!;
Console.WriteLine($&quot;Default: Total = {loose.Total}&quot;);

try { JsonSerializer.Deserialize&lt;Invoice&gt;(json, JsonSerializerOptions.Strict); }
catch (JsonException ex) { Console.WriteLine($&quot;Strict:  {ex.Message}&quot;); }

string extra = &quot;&quot;&quot;{ &quot;Customer&quot;: &quot;Ryan Carter&quot;, &quot;Total&quot;: 49.99, &quot;Coupon&quot;: &quot;FALL26&quot; }&quot;&quot;&quot;;
try { JsonSerializer.Deserialize&lt;Invoice&gt;(extra, JsonSerializerOptions.Strict); }
catch (JsonException ex) { Console.WriteLine($&quot;Strict:  {ex.Message}&quot;); }

record Invoice(string Customer, decimal Total);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>Default: Total = 0.01
Strict:  Duplicate property &#x27;Total&#x27; encountered during deserialization of type &#x27;Invoice&#x27;.
Strict:  The JSON property &#x27;Coupon&#x27; could not be mapped to any .NET member contained in type &#x27;Invoice&#x27;.</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-jsonserializeroptions-strict-duplicate-property-1.png" alt="VS Code running StrictJson.cs on .NET 10: default options keep the last duplicate Total while JsonSerializerOptions.Strict throws JsonException" class="wp-image-2377" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-jsonserializeroptions-strict-duplicate-property-1.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-jsonserializeroptions-strict-duplicate-property-1-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-jsonserializeroptions-strict-duplicate-property-1-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-jsonserializeroptions-strict-duplicate-property-1-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">Default options accept the duplicate Total; Strict rejects it and the unknown Coupon field</figcaption></figure>
</div>


<h3 class="wp-block-heading">How does this code work?</h3>



<ul class="wp-block-list">
<li>The default deserializer reads <code>Total</code> twice and keeps <code>0.01</code>. Imagine that on a $49.99 invoice.</li>

<li><code>JsonSerializerOptions.Strict</code> disallows duplicate properties and unmapped members, keeps case-sensitive binding, and respects nullable annotations and required constructor parameters.</li>

<li>Each violation throws <code>JsonException</code> with a clear message, so you can return a 400 instead of saving bad data.</li>
</ul>



<p class="wp-block-paragraph">I catch the error with a normal <a href="https://aspdotnethelp.com/try-catch-in-csharp/">try-catch block in C#</a>. For the writing side of JSON, see how to <a href="https://aspdotnethelp.com/convert-object-to-json-without-escape-characters-in-csharp/">convert an object to JSON without escape characters</a>.</p>



<h2 class="wp-block-heading">Feature 7: New .NET 10 Library APIs You Can Use Today</h2>



<p class="wp-block-paragraph">The libraries got dozens of additions, from post-quantum cryptography to async ZIP APIs. These three solve problems I&#8217;ve hit in real business code:</p>



<pre class="wp-block-code"><code>using System.Globalization;

var numeric = StringComparer.Create(CultureInfo.GetCultureInfo(&quot;en-US&quot;), CompareOptions.NumericOrdering);
string[] ids = [&quot;INV-10&quot;, &quot;INV-2&quot;, &quot;INV-1&quot;];
Console.WriteLine(&quot;Ordinal: &quot; + string.Join(&quot;, &quot;, ids.Order(StringComparer.Ordinal)));
Console.WriteLine(&quot;Numeric: &quot; + string.Join(&quot;, &quot;, ids.Order(numeric)));

var stock = new OrderedDictionary&lt;string, int&gt; { [&quot;Austin&quot;] = 4 };
foreach (var city in new[] { &quot;Denver&quot;, &quot;Austin&quot; })
    if (!stock.TryAdd(city, 1, out int i))
        stock.SetAt(i, stock.GetAt(i).Value + 1);
Console.WriteLine(string.Join(&quot;, &quot;, stock.Select(kv =&gt; $&quot;{kv.Key}={kv.Value}&quot;)));

var thanksgiving = new DateOnly(2026, 11, 26);
Console.WriteLine($&quot;Thanksgiving 2026 is in ISO week {ISOWeek.GetWeekOfYear(thanksgiving)}&quot;);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>Ordinal: INV-1, INV-10, INV-2
Numeric: INV-1, INV-2, INV-10
Austin=5, Denver=1
Thanksgiving 2026 is in ISO week 48</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-numeric-ordering-ordereddictionary-isoweek.png" alt="VS Code running NewApis.cs on .NET 10: CompareOptions.NumericOrdering sorts INV-2 before INV-10, OrderedDictionary TryAdd with index and ISOWeek with DateOnly" class="wp-image-2062" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-numeric-ordering-ordereddictionary-isoweek.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-numeric-ordering-ordereddictionary-isoweek-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-numeric-ordering-ordereddictionary-isoweek-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-10-numeric-ordering-ordereddictionary-isoweek-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">Numeric ordering, OrderedDictionary.TryAdd with an index, and ISOWeek for DateOnly</figcaption></figure>
</div>


<h3 class="wp-block-heading">How does this code work?</h3>



<p class="wp-block-paragraph"><code>CompareOptions.NumericOrdering</code> compares digits as numbers, so INV-2 sorts before INV-10. An ordinal sort puts INV-10 second.</p>



<p class="wp-block-paragraph">The new <code>TryAdd(key, value, out int index)</code> overload returns the existing entry&#8217;s index when the key is taken, so <code>SetAt</code> updates it without a second lookup.</p>



<p class="wp-block-paragraph"><code>ISOWeek.GetWeekOfYear</code> now accepts <code>DateOnly</code>. Thanksgiving 2026 (November 26) lands in ISO week 48.</p>



<p class="wp-block-paragraph">Numeric ordering removes the need for a custom comparer when you <a href="https://aspdotnethelp.com/sort-an-array-in-csharp/">sort an array in C#</a>. If <code>OrderedDictionary</code> is new to you, start with the basics of the <a href="https://aspdotnethelp.com/dictionary-in-csharp/">Dictionary in C#</a>.</p>



<h2 class="wp-block-heading">Feature 8: .NET 10 SDK and CLI Changes</h2>



<p class="wp-block-paragraph">A few SDK changes don&#8217;t need a demo file, but they change daily habits. Here&#8217;s what I verified in the .NET 10 SDK notes:</p>



<figure class="wp-block-table"><table><thead><tr><th>Command or setting</th><th>What it does</th></tr></thead><tbody><tr><td><code>dotnet tool exec dotnetsay</code></td><td>Downloads and runs a .NET tool once, without installing it globally or locally</td></tr><tr><td><code>dnx dotnetsay "Hello"</code></td><td>A shorter script that forwards to <code>dotnet tool exec</code></td></tr><tr><td><code>dotnet package add</code></td><td>Noun-first alias for <code>dotnet add package</code> (also <code>package list/remove</code>, <code>reference add/list/remove</code>)</td></tr><tr><td><code>dotnet publish app.cs</code></td><td>Publishes a file-based app as a native AOT executable</td></tr><tr><td><code>dotnet completions script pwsh</code></td><td>Generates native tab completion for PowerShell, bash, zsh, fish and nushell</td></tr><tr><td><code>--cli-schema</code></td><td>Prints any command&#8217;s options as JSON, handy for tooling</td></tr><tr><td><code>dotnet publish /t:PublishContainer</code></td><td>Console apps build container images with no extra project property</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">On my machine the SDK installed <code>dnx.cmd</code> right next to <code>dotnet.exe</code> in <code>C:\Program Files\dotnet</code>. Older SDKs don&#8217;t have it, so run <code>dotnet --version</code> first. Tooling questions like these also show up in <a href="https://aspdotnethelp.com/csharp-interview-questions-and-answers-for-experienced-professionals/">C# interview questions for experienced developers</a>.</p>



<h2 class="wp-block-heading">What Else Is New in .NET 10 for ASP.NET Core and Blazor?</h2>



<p class="wp-block-paragraph">Not everything fits in a single-file demo. These are the other ASP.NET Core 10 and platform changes I&#8217;d read about before a project kicks off:</p>



<figure class="wp-block-table"><table><thead><tr><th>Feature</th><th>What changed</th></tr></thead><tbody><tr><td>Passkeys in Identity</td><td>ASP.NET Core Identity supports passkeys (WebAuthn/FIDO2), and the Blazor Web App template includes passkey sign-in and management</td></tr><tr><td><code>[PersistentState]</code></td><td>Blazor persists prerendered component state declaratively instead of through <code>PersistentComponentState</code> code</td></tr><tr><td><code>ResourcePreloader</code></td><td>Blazor Web Apps preload WebAssembly assets from <code>App.razor</code></td></tr><tr><td><code>NavigationManager.NotFound()</code></td><td>Returns a 404 in static SSR and renders a <code>NotFoundPage</code> in interactive mode</td></tr><tr><td>Cookie auth on APIs</td><td>Known API endpoints return 401/403 instead of redirecting to a login page</td></tr><tr><td>Memory pool eviction</td><td>Kestrel, IIS and HTTP.sys release idle memory automatically</td></tr><tr><td>EF Core 10</td><td>Named query filters let one entity have several filters you can switch off one at a time</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The cookie change matters most if your SPA used to get a login page back instead of a 401. If you&#8217;re building sign-in today, passkeys are worth a look before you add a one-time code flow like this <a href="https://aspdotnethelp.com/generate-otp-asp-net/">OTP generator in ASP.NET</a>.</p>



<h2 class="wp-block-heading">Should You Upgrade to .NET 10 Now?</h2>



<p class="wp-block-paragraph">Yes, for nearly everyone. .NET 8 and .NET 9 both lose support on November 10, 2026, and .NET 10 is covered until November 14, 2028. Changing <code>net8.0</code> to <code>net10.0</code> is usually the easy part; package updates and breaking changes take the time.</p>



<p class="wp-block-paragraph">I list every step, from <code>global.json</code> to Docker images, in my guide to <a href="https://aspdotnethelp.com/upgrade-dotnet-8-to-dotnet-10/">upgrade .NET 8 to .NET 10</a>. Start with file-based apps for scripts, <code>AddValidation()</code> for Minimal APIs, and <code>JsonSerializerOptions.Strict</code> anywhere money changes hands.</p>



<p class="wp-block-paragraph">If you&#8217;re picking up .NET 10 alongside the basics, these are good next reads:</p>



<ul class="wp-block-list">
<li><a href="https://aspdotnethelp.com/what-is-csharp/">What is C# and what it&#8217;s used for</a></li>

<li><a href="https://aspdotnethelp.com/list-in-csharp/">Working with a List in C#</a></li>

<li><a href="https://aspdotnethelp.com/interface-in-csharp/">Interfaces in C#, explained with examples</a></li>

</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-nt0000001" class="rank-math-list-item">
<h3 class="rank-math-question ">Is .NET 10 an LTS release?</h3>
<div class="rank-math-answer ">

<p>Yes. .NET 10 is a long-term support release that shipped on November 11, 2025, and Microsoft supports it until November 14, 2028.</p>

</div>
</div>
<div id="faq-question-nt0000002" class="rank-math-list-item">
<h3 class="rank-math-question ">What C# version comes with .NET 10?</h3>
<div class="rank-math-answer ">

<p>C# 14. It adds the <code>field</code> keyword, extension members (including extension properties), null-conditional assignment, implicit span conversions and more.</p>

</div>
</div>
<div id="faq-question-nt0000003" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I run a C# file without a project in .NET 10?</h3>
<div class="rank-math-answer ">

<p>Yes. Run <code>dotnet run app.cs</code> (or <code>dotnet app.cs</code>). Use <code>#:package</code>, <code>#:sdk</code>, <code>#:property</code> and <code>#:project</code> directives at the top of the file instead of a .csproj.</p>

</div>
</div>
<div id="faq-question-nt0000004" class="rank-math-list-item">
<h3 class="rank-math-question ">Why does my file-based app say reflection-based serialization is disabled?</h3>
<div class="rank-math-answer ">

<p>File-based apps target native AOT by default, which turns off reflection-based System.Text.Json. Add <code>#:property PublishAot=false</code> at the top of the file, or use a JSON source generator context.</p>

</div>
</div>
<div id="faq-question-nt0000005" class="rank-math-list-item">
<h3 class="rank-math-question ">Does ASP.NET Core 10 validate Minimal API parameters?</h3>
<div class="rank-math-answer ">

<p>Yes, once you call <code>builder.Services.AddValidation()</code>. Invalid requests get a 400 response listing each error. Microsoft covers the details in <a href="https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-10.0" target="_blank" rel="noopener">What&#8217;s new in ASP.NET Core in .NET 10</a>.</p>

</div>
</div>
<div id="faq-question-nt0000006" class="rank-math-list-item">
<h3 class="rank-math-question ">Where can I see the full list of .NET 10 changes?</h3>
<div class="rank-math-answer ">

<p>Microsoft keeps it on <a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview" target="_blank" rel="noopener">What&#8217;s new in .NET 10</a> on Microsoft Learn, with separate pages for the runtime, libraries and SDK.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img alt='Bijay Kumar' src='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=100&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=200&#038;d=mm&#038;r=g 2x' class='avatar avatar-100 photo' height='100' width='100' itemprop="image"/></div><div class="saboxplugin-authorname"><a href="https://aspdotnethelp.com/bijay-kumar/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Bijay Kumar is a renowned software engineer, accomplished author, and distinguished <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000972" rel="nofollow noopener" target="_blank">Microsoft Most Valuable Professional</a> (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. <a href="https://aspdotnethelp.com/about/" rel="nofollow noopener">Read more&#8230;</a></p>
</div></div><div class="saboxplugin-web "><a href="https://aspdotnethelp.com" target="_self">aspdotnethelp.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials "><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-linkedin" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 448 512"><path fill="currentColor" d="M100.3 480H7.4V180.9h92.9V480zM53.8 140.1C24.1 140.1 0 115.5 0 85.8 0 56.1 24.1 32 53.8 32c29.7 0 53.8 24.1 53.8 53.8 0 29.7-24.1 54.3-53.8 54.3zM448 480h-92.7V334.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V480h-92.8V180.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V480z"></path></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 264 512"><path fill="currentColor" d="M76.7 512V283H0v-91h76.7v-71.7C76.7 42.4 124.3 0 193.8 0c33.3 0 61.9 2.5 70.2 3.6V85h-48.2c-37.8 0-45.1 18-45.1 44.3V192H256l-11.7 91h-73.6v229"></path></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/c/enjoysharepoint?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 30 30"><path d="M26.37,26l-8.795-12.822l0.015,0.012L25.52,4h-2.65l-6.46,7.48L11.28,4H4.33l8.211,11.971L12.54,15.97L3.88,26h2.65 l7.182-8.322L19.42,26H26.37z M10.23,6l12.34,18h-2.1L8.12,6H10.23z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>.NET 8 and .NET 9 End of Support (November 10, 2026): What It Means and How to Move to .NET 10</title>
		<link>https://aspdotnethelp.com/dotnet-8-9-end-of-support/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 26 Oct 2026 17:46:18 +0000</pubDate>
				<category><![CDATA[ASP.NET Core]]></category>
		<guid isPermaLink="false">https://aspdotnethelp.com/?p=2043</guid>

					<description><![CDATA[.NET 8 and .NET 9 both reach end of support on November 10, 2026. After that Patch Tuesday, Microsoft stops shipping security fixes for them, and your apps keep running unpatched. The fix is .NET 10, an LTS release supported until November 14, 2028. In this tutorial, I&#8217;ll break down what .NET 8 end of ... <a title=".NET 8 and .NET 9 End of Support (November 10, 2026): What It Means and How to Move to .NET 10" class="read-more" href="https://aspdotnethelp.com/dotnet-8-9-end-of-support/" aria-label="Read more about .NET 8 and .NET 9 End of Support (November 10, 2026): What It Means and How to Move to .NET 10">Read more &#62;&#62;</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">.NET 8 and .NET 9 both reach end of support on November 10, 2026. After that Patch Tuesday, Microsoft stops shipping security fixes for them, and your apps keep running unpatched. The fix is .NET 10, an LTS release supported until November 14, 2028.</p>



<p class="wp-block-paragraph">In this tutorial, I&#8217;ll break down what <strong>.NET 8 end of support</strong> means in practice, who&#8217;s affected, the full version timeline, the Azure Functions deadline that lands the same day, and the short path to .NET 10.</p>



<h2 class="wp-block-heading">.NET 8 and .NET 9 End of Support Dates at a Glance</h2>



<p class="wp-block-paragraph">Here&#8217;s the timeline from Microsoft&#8217;s .NET support policy, with the status as of today, October 26, 2026. My <a href="https://aspdotnethelp.com/dotnet-versions/">.NET versions and support dates</a> page tracks every release:</p>



<figure class="wp-block-table"><table><thead><tr><th>Version</th><th>Type</th><th>Released</th><th>End of support</th><th>Status</th></tr></thead><tbody><tr><td>.NET 10</td><td>LTS</td><td>Nov 11, 2025</td><td>Nov 14, 2028</td><td>Supported</td></tr><tr><td>.NET 9</td><td>STS</td><td>Nov 12, 2024</td><td>Nov 10, 2026</td><td>Ends in 15 days</td></tr><tr><td>.NET 8</td><td>LTS</td><td>Nov 14, 2023</td><td>Nov 10, 2026</td><td>Ends in 15 days</td></tr><tr><td>.NET 7</td><td>STS</td><td>Nov 8, 2022</td><td>May 14, 2024</td><td>Out of support</td></tr><tr><td>.NET 6</td><td>LTS</td><td>Nov 8, 2021</td><td>Nov 12, 2024</td><td>Out of support</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">I like having the countdown in code, so here&#8217;s a tiny .NET 10 file-based app. It stores the dates in a <a href="https://aspdotnethelp.com/dictionary-in-csharp/">C# Dictionary</a> and subtracts <code>DateOnly.DayNumber</code> values:</p>



<pre class="wp-block-code"><code>using System.Globalization;
using System.Runtime.InteropServices;

CultureInfo.CurrentCulture = new(&quot;en-US&quot;);
var today = new DateOnly(2026, 10, 26);   // fixed &quot;today&quot;
var ends = new Dictionary&lt;string, DateOnly&gt;
{
    [&quot;.NET 8 (LTS)&quot;] = new(2026, 11, 10),
    [&quot;.NET 9 (STS)&quot;] = new(2026, 11, 10),
    [&quot;.NET 10 (LTS)&quot;] = new(2028, 11, 14),
};

Console.WriteLine($&quot;Runtime: {RuntimeInformation.FrameworkDescription}&quot;);
foreach (var (version, end) in ends)
{
    int days = end.DayNumber - today.DayNumber;
    Console.WriteLine($&quot;{version,-14} {end:MMM d, yyyy}: {days} days left&quot;);
}</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>Runtime: .NET 10.0.13
.NET 8 (LTS)   Nov 10, 2026: 15 days left
.NET 9 (STS)   Nov 10, 2026: 15 days left
.NET 10 (LTS)  Nov 14, 2028: 750 days left</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-8-9-end-of-support-days-left-csharp.png" alt="VS Code running SupportCheck.cs: a C# program printing days left until .NET 8 and .NET 9 end of support on November 10, 2026" class="wp-image-2055" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-8-9-end-of-support-days-left-csharp.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-8-9-end-of-support-days-left-csharp-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-8-9-end-of-support-days-left-csharp-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-8-9-end-of-support-days-left-csharp-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">15 days left for .NET 8 and .NET 9, counted from October 26, 2026</figcaption></figure>
</div>


<p class="wp-block-paragraph"><code>DayNumber</code> turns each date into a plain day count, so the subtraction can&#8217;t trip over month lengths. I use the same idea for <a href="https://aspdotnethelp.com/get-start-and-end-date-of-month-in-csharp/">start and end dates of a month in C#</a>.</p>



<h2 class="wp-block-heading">What Does .NET 8 End of Support Actually Mean?</h2>



<p class="wp-block-paragraph">Microsoft defines end of support as the date after which it no longer provides fixes, updates or technical assistance. In practice, that&#8217;s four things:</p>



<ul class="wp-block-list">
<li><strong>No security patches.</strong> .NET servicing updates ship monthly, and security fixes land on Patch Tuesday (the second Tuesday). After November 10, .NET 8 and .NET 9 get none.</li>

<li><strong>One possible last update.</strong> November 10 is itself a Patch Tuesday, so each version may get a final update that day, but only for a known critical issue.</li>

<li><strong>No Microsoft support.</strong> You can&#8217;t open a support case for an out-of-support runtime.</li>

<li><strong>Your apps keep running.</strong> Nothing gets switched off. The risk is the next vulnerability that never gets patched.</li>
</ul>



<p class="wp-block-paragraph">Visual Studio 2022 will mark the .NET 8 and .NET 9 components as out of support in a future servicing update. Existing installs aren&#8217;t affected, though the installer can remove out-of-support components.</p>



<p class="wp-block-paragraph">On Azure App Service, apps on an end-of-support runtime continue to run unchanged. But App Service can&#8217;t provide security patches or support for that runtime until you upgrade.</p>



<p class="wp-block-paragraph">Compliance is the part that bites first. Vulnerability scanners and auditors usually flag unsupported runtimes, so expect questions in your next security review. It&#8217;s also a common topic in <a href="https://aspdotnethelp.com/dot-net-core-interview-questions-and-answers/">.NET Core interview questions</a>.</p>



<h2 class="wp-block-heading">Why Do .NET 8 and .NET 9 End Support on the Same Day?</h2>



<p class="wp-block-paragraph">Releases alternate between Long Term Support and Standard Term Support. LTS versions get at least three years of patches. STS versions used to get 18 months, but Microsoft extended STS to 24 months in late 2025.</p>



<p class="wp-block-paragraph">That extension moved .NET 9 from May 2026 to November 2026, right on top of .NET 8. End dates also snap to Patch Tuesday, which is why it&#8217;s November 10 rather than a release anniversary.</p>



<p class="wp-block-paragraph">So there&#8217;s no point hopping from .NET 8 to .NET 9 now. Go straight to .NET 10 and you get support until 2028, plus everything in <a href="https://aspdotnethelp.com/whats-new-in-dotnet-10/">what is new in .NET 10</a>.</p>



<h2 class="wp-block-heading">Who Is Affected by .NET 8 and .NET 9 End of Support?</h2>



<p class="wp-block-paragraph">If any of these describe your work, the November 10 date applies to you:</p>



<ul class="wp-block-list">
<li>Apps whose project files target <code>net8.0</code> or <code>net9.0</code>, whether framework-dependent or self-contained.</li>

<li>Docker images built on the 8.0 or 9.0 runtime tags.</li>

<li>Web apps on Azure App Service using the .NET 8 or .NET 9 stack.</li>

<li>Azure Functions on the in-process model (covered below).</li>

<li>Third-party tools built on .NET 8 or 9. Ask the vendor for a .NET 10 build.</li>
</ul>



<p class="wp-block-paragraph">.NET Framework 4.8 isn&#8217;t part of this. It follows a separate lifecycle tied to the Windows version it ships with.</p>



<h2 class="wp-block-heading">How to Check Which .NET Version You&#8217;re Running</h2>



<p class="wp-block-paragraph">Start with the machine. <code>dotnet --version</code> shows the SDK the CLI picks, and <code>dotnet --list-runtimes</code> lists every installed runtime. My full guide to <a href="https://aspdotnethelp.com/check-dotnet-version/">checking the .NET version</a> covers Visual Studio, the registry and code too.</p>



<p class="wp-block-paragraph">Here&#8217;s what my demo machine prints for the runtimes:</p>



<pre class="wp-block-code"><code>dotnet --list-runtimes
Microsoft.AspNetCore.App 6.0.24 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 10.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.24 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 10.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.24 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 10.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]</code></pre>



<p class="wp-block-paragraph">Then check the code. Search your repos for <code>&lt;TargetFramework&gt;net8.0</code> and <code>net9.0</code>, including <code>TargetFrameworks</code> with an s for multi-targeted libraries. Don&#8217;t forget CI pipelines and Dockerfiles that pin an SDK or image version.</p>



<p class="wp-block-paragraph">For a build-time alarm, set the <code>CheckSdkVulnerabilities</code> MSBuild property to <code>true</code>. The build then warns with NETSDK1239 when the resolved SDK is end of life.</p>



<h2 class="wp-block-heading">Azure Functions In-Process Model Support Also Ends November 10, 2026</h2>



<p class="wp-block-paragraph">This one&#8217;s easy to miss. Support for the Azure Functions in-process model ends on the same day, and Microsoft&#8217;s answer is to migrate C# function apps to the isolated worker model on .NET 10.</p>



<p class="wp-block-paragraph">The migration touches more than the target framework. Here are the main changes from the Microsoft Learn migration guide:</p>



<ol class="wp-block-list">
<li>Set the project SDK to <code>Azure.Functions.Sdk</code> and <code>TargetFramework</code> to <code>net10.0</code>.</li>

<li>Replace <code>Microsoft.NET.Sdk.Functions</code> and any <code>Microsoft.Azure.WebJobs.*</code> packages with <code>Microsoft.Azure.Functions.Worker</code> packages.</li>

<li>Add a <code>Program.cs</code> that builds the host; it replaces your <code>FunctionsStartup</code> class.</li>

<li>Rename <code>[FunctionName]</code> to <code>[Function]</code> and get <code>ILogger&lt;T&gt;</code> through the constructor.</li>

<li>Set <code>FUNCTIONS_WORKER_RUNTIME</code> to <code>dotnet-isolated</code>, then deploy through a staging slot and swap.</li>
</ol>



<p class="wp-block-paragraph">Locally, that last setting lives in <code>local.settings.json</code>:</p>



<pre class="wp-block-code"><code>{
    &quot;IsEncrypted&quot;: false,
    &quot;Values&quot;: {
        &quot;AzureWebJobsStorage&quot;: &quot;UseDevelopmentStorage=true&quot;,
        &quot;FUNCTIONS_WORKER_RUNTIME&quot;: &quot;dotnet-isolated&quot;
    }
}</code></pre>



<p class="wp-block-paragraph">Watch the JSON, too. The isolated model uses System.Text.Json, which ignores Newtonsoft attributes like <code>[JsonProperty]</code>. I covered its options in <a href="https://aspdotnethelp.com/convert-object-to-json-without-escape-characters-in-csharp/">serializing objects to JSON in C#</a>.</p>



<h2 class="wp-block-heading">How to Move from .NET 8 or .NET 9 to .NET 10</h2>



<p class="wp-block-paragraph">For most apps, the upgrade is a short checklist. I&#8217;ll keep it brief here; the step-by-step version with real errors and fixes is in my <a href="https://aspdotnethelp.com/upgrade-dotnet-8-to-dotnet-10/">guide to upgrading .NET 8 to .NET 10</a>.</p>



<ol class="wp-block-list">
<li>Install the .NET 10 SDK and pin it with <code>global.json</code> so every developer and build agent uses the same one.</li>

<li>Change <code>TargetFramework</code> from <code>net8.0</code> or <code>net9.0</code> to <code>net10.0</code>.</li>

<li>Update <code>Microsoft.*</code> packages (EF Core, ASP.NET Core, Extensions) to 10.x; <code>dotnet list package --outdated</code> shows what&#8217;s behind.</li>

<li>Build, run your tests and read the .NET 10 breaking changes for the areas you use.</li>

<li>Update hosting: the App Service stack, Docker base images and CI pipeline SDK versions.</li>

<li>Deploy to staging first, then production.</li>
</ol>



<h3 class="wp-block-heading">Pin the SDK with global.json</h3>



<p class="wp-block-paragraph">This <code>global.json</code> asks for SDK 10.0.100 or any later 10.0 feature band, so the CLI won&#8217;t silently fall back to an older SDK:</p>



<pre class="wp-block-code"><code>{
  &quot;sdk&quot;: {
    &quot;version&quot;: &quot;10.0.100&quot;,
    &quot;rollForward&quot;: &quot;latestFeature&quot;
  }
}</code></pre>



<p class="wp-block-paragraph">Running <code>dotnet --version</code> and <code>dotnet --list-sdks</code> in that folder confirms the pick:</p>



<pre class="wp-block-code"><code>dotnet --version
10.0.402

dotnet --list-sdks
7.0.403 [C:\Program Files\dotnet\sdk]
10.0.402 [C:\Program Files\dotnet\sdk]</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/global-json-dotnet-10-sdk-version-output.png" alt="VS Code showing global.json pinning the .NET 10 SDK with rollForward latestFeature, and dotnet --version and dotnet --list-sdks output" class="wp-image-2057" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/global-json-dotnet-10-sdk-version-output.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/global-json-dotnet-10-sdk-version-output-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/global-json-dotnet-10-sdk-version-output-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/global-json-dotnet-10-sdk-version-output-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">global.json resolves to SDK 10.0.402, the newest installed 10.0 SDK</figcaption></figure>
</div>


<h3 class="wp-block-heading">Change the TargetFramework to net10.0</h3>



<p class="wp-block-paragraph">Here&#8217;s a small console project after the change. The comment marks where <code>net8.0</code> used to be. If you&#8217;re new to project-based apps, see <a href="https://aspdotnethelp.com/create-a-console-application-in-visual-studio-code/">creating a console app in VS Code</a>.</p>



<pre class="wp-block-code"><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;

  &lt;PropertyGroup&gt;
    &lt;OutputType&gt;Exe&lt;/OutputType&gt;
    &lt;!-- was net8.0: .NET 8 support ends November 10, 2026 --&gt;
    &lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
  &lt;/PropertyGroup&gt;

&lt;/Project&gt;</code></pre>



<p class="wp-block-paragraph">And the <code>Program.cs</code> that proves which runtime it lands on:</p>



<pre class="wp-block-code"><code>using System.Runtime.InteropServices;

Console.WriteLine($&quot;Framework: {RuntimeInformation.FrameworkDescription}&quot;);
Console.WriteLine($&quot;Runtime version: {Environment.Version}&quot;);
Console.WriteLine($&quot;Target: {AppContext.TargetFrameworkName}&quot;);</code></pre>



<p class="wp-block-paragraph">Running <code>dotnet run --project OrdersApp</code> prints:</p>



<pre class="wp-block-code"><code>Framework: .NET 10.0.13
Runtime version: 10.0.13
Target: .NETCoreApp,Version=v10.0</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/csproj-targetframework-net10-upgrade-output.png" alt="VS Code showing OrdersApp.csproj with TargetFramework net10.0 and dotnet run output reporting .NETCoreApp v10.0" class="wp-image-2054" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/csproj-targetframework-net10-upgrade-output.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/csproj-targetframework-net10-upgrade-output-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/csproj-targetframework-net10-upgrade-output-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/csproj-targetframework-net10-upgrade-output-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">After the TargetFramework change, the app reports .NET 10</figcaption></figure>
</div>


<p class="wp-block-paragraph"><code>AppContext.TargetFrameworkName</code> confirms the build target, while <code>Environment.Version</code> shows the runtime patch. With .NET 10 you also get C# 14 by default; see <a href="https://aspdotnethelp.com/whats-new-in-csharp-14/">what is new in C# 14</a>.</p>



<h2 class="wp-block-heading">Which .NET 8 End of Support Plan Should You Pick?</h2>



<p class="wp-block-paragraph">With 15 days left, here&#8217;s how I&#8217;d triage:</p>



<figure class="wp-block-table"><table><thead><tr><th>Your situation</th><th>What I&#8217;d do now</th></tr></thead><tbody><tr><td>Simple web app or API on .NET 8/9</td><td>Retarget to net10.0 this week and ship it</td></tr><tr><td>Azure Functions in-process</td><td>Migrate to isolated worker on .NET 10 first</td></tr><tr><td>Large app, can&#8217;t finish by Nov 10</td><td>Start now, patch to the final 8.0 update, plan a dated cutover</td></tr><tr><td>Vendor app on .NET 8</td><td>Ask the vendor for a .NET 10 release</td></tr><tr><td>.NET Framework 4.8 app</td><td>Not affected by this date</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Whatever you pick, log errors well during the cutover.</p>



<p class="wp-block-paragraph">Once you&#8217;re on .NET 10, these are good next reads:</p>



<ul class="wp-block-list">
<li><a href="https://aspdotnethelp.com/what-is-csharp/">What C# is and how it fits with .NET 10</a></li>

<li><a href="https://aspdotnethelp.com/try-catch-in-csharp/">Handling errors with try-catch in C#</a></li>

<li><a href="https://aspdotnethelp.com/tutorials/csharp-net/">All C# and .NET tutorials</a></li>

<li><a href="https://aspdotnethelp.com/asp-net-core-tutorials/">ASP.NET Core tutorials</a></li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-eo0000001" class="rank-math-list-item">
<h3 class="rank-math-question ">Will my .NET 8 app stop working after November 10, 2026?</h3>
<div class="rank-math-answer ">

<p>No. It keeps running. It just stops receiving security fixes and Microsoft support, so new vulnerabilities in the runtime stay unpatched.</p>

</div>
</div>
<div id="faq-question-eo0000002" class="rank-math-list-item">
<h3 class="rank-math-question ">Is .NET 9 supported longer than .NET 8?</h3>
<div class="rank-math-answer ">

<p>No. Even though .NET 9 is newer, it&#8217;s an STS release with 24 months of support, so it ends on November 10, 2026, the same day as .NET 8.</p>

</div>
</div>
<div id="faq-question-eo0000003" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I upgrade from .NET 8 straight to .NET 10?</h3>
<div class="rank-math-answer ">

<p>Yes. You don&#8217;t need .NET 9 in between. Change TargetFramework to net10.0, update your packages and review the .NET 10 breaking changes.</p>

</div>
</div>
<div id="faq-question-eo0000004" class="rank-math-list-item">
<h3 class="rank-math-question ">How long is .NET 10 supported?</h3>
<div class="rank-math-answer ">

<p>Until November 14, 2028. It&#8217;s an LTS release, per the <a href="https://learn.microsoft.com/en-us/dotnet/core/releases-and-support" target="_blank" rel="noopener">.NET releases and support</a> page on Microsoft Learn.</p>

</div>
</div>
<div id="faq-question-eo0000005" class="rank-math-list-item">
<h3 class="rank-math-question ">Does the Azure Functions in-process model end too?</h3>
<div class="rank-math-answer ">

<p>Yes, on the same day. Move C# function apps to the isolated worker model, following the <a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-dotnet-to-isolated-model" target="_blank" rel="noopener">in-process to isolated migration guide</a>.</p>

</div>
</div>
<div id="faq-question-eo0000006" class="rank-math-list-item">
<h3 class="rank-math-question ">Is .NET Framework 4.8 affected?</h3>
<div class="rank-math-answer ">

<p>No. .NET Framework has its own lifecycle tied to Windows, separate from .NET 8 and .NET 9.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img alt='Bijay Kumar' src='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=100&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=200&#038;d=mm&#038;r=g 2x' class='avatar avatar-100 photo' height='100' width='100' itemprop="image"/></div><div class="saboxplugin-authorname"><a href="https://aspdotnethelp.com/bijay-kumar/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Bijay Kumar is a renowned software engineer, accomplished author, and distinguished <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000972" rel="nofollow noopener" target="_blank">Microsoft Most Valuable Professional</a> (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. <a href="https://aspdotnethelp.com/about/" rel="nofollow noopener">Read more&#8230;</a></p>
</div></div><div class="saboxplugin-web "><a href="https://aspdotnethelp.com" target="_self">aspdotnethelp.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials "><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-linkedin" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 448 512"><path fill="currentColor" d="M100.3 480H7.4V180.9h92.9V480zM53.8 140.1C24.1 140.1 0 115.5 0 85.8 0 56.1 24.1 32 53.8 32c29.7 0 53.8 24.1 53.8 53.8 0 29.7-24.1 54.3-53.8 54.3zM448 480h-92.7V334.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V480h-92.8V180.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V480z"></path></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 264 512"><path fill="currentColor" d="M76.7 512V283H0v-91h76.7v-71.7C76.7 42.4 124.3 0 193.8 0c33.3 0 61.9 2.5 70.2 3.6V85h-48.2c-37.8 0-45.1 18-45.1 44.3V192H256l-11.7 91h-73.6v229"></path></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/c/enjoysharepoint?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 30 30"><path d="M26.37,26l-8.795-12.822l0.015,0.012L25.52,4h-2.65l-6.46,7.48L11.28,4H4.33l8.211,11.971L12.54,15.97L3.88,26h2.65 l7.182-8.322L19.42,26H26.37z M10.23,6l12.34,18h-2.1L8.12,6H10.23z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>.NET Core Interview Questions and Answers (2026, .NET 10)</title>
		<link>https://aspdotnethelp.com/dot-net-core-interview-questions-and-answers/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Fri, 19 May 2023 10:44:21 +0000</pubDate>
				<category><![CDATA[ASP.NET Core]]></category>
		<guid isPermaLink="false">https://aspdotnethelp.com/?p=285</guid>

					<description><![CDATA[Job posts in Seattle, Austin and New York still say &#8220;.NET Core developer,&#8221; but the .NET Core interview questions you&#8217;ll hear in 2026 are about .NET 10, ASP.NET Core, dependency injection, middleware and EF Core. The &#8220;Core&#8221; name was dropped after 3.1. In this tutorial, I&#8217;ll answer 40 .NET Core interview questions with short answers ... <a title=".NET Core Interview Questions and Answers (2026, .NET 10)" class="read-more" href="https://aspdotnethelp.com/dot-net-core-interview-questions-and-answers/" aria-label="Read more about .NET Core Interview Questions and Answers (2026, .NET 10)">Read more &#62;&#62;</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Job posts in Seattle, Austin and New York still say &#8220;.NET Core developer,&#8221; but the .NET Core interview questions you&#8217;ll hear in 2026 are about .NET 10, ASP.NET Core, dependency injection, middleware and EF Core. The &#8220;Core&#8221; name was dropped after 3.1.</p>



<p class="wp-block-paragraph">In this tutorial, I&#8217;ll answer 40 <strong>.NET Core interview questions</strong> with short answers first, then the detail an interviewer wants to hear. Each group is labeled Junior, Mid or Senior.</p>



<p class="wp-block-paragraph">For the trickiest ones, like DI lifetimes and middleware order, I ran a small .NET 10 program so you can see the proof, not just my word for it.</p>



<h2 class="wp-block-heading">.NET Platform Interview Questions (Junior)</h2>



<h3 class="wp-block-heading">Q1. What is .NET Core, and what is it called now? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> .NET Core was the cross-platform, open-source rewrite of .NET. Starting with .NET 5, Microsoft dropped &#8220;Core&#8221; from the name, so today it&#8217;s just .NET, and the current long-term release is .NET 10.</p>



<p class="wp-block-paragraph">ASP.NET Core and EF Core kept &#8220;Core,&#8221; so job posts still use it. C# is the main language on top of it; if you need a refresher, my article on <a href="https://aspdotnethelp.com/what-is-csharp/">what C# is and how it runs on .NET</a> covers the basics.</p>



<h3 class="wp-block-heading">Q2. What&#8217;s the difference between .NET and .NET Framework? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> .NET Framework 4.8.x is Windows-only and only gets fixes. Modern .NET runs on Windows, Linux and macOS, ships a new version every November and is where every new feature lands.</p>



<figure class="wp-block-table"><table><thead><tr><th></th><th>.NET Framework 4.8.x</th><th>.NET 10</th></tr></thead><tbody><tr><td>Platforms</td><td>Windows only</td><td>Windows, Linux, macOS</td></tr><tr><td>Web stack</td><td>ASP.NET (System.Web), Web Forms, MVC 5</td><td>ASP.NET Core (Kestrel, minimal APIs, Blazor)</td></tr><tr><td>Side-by-side installs</td><td>No, one per machine</td><td>Yes, and self-contained apps</td></tr><tr><td>New features</td><td>None, servicing only</td><td>Every yearly release</td></tr><tr><td>Containers</td><td>Windows containers only</td><td>Small Linux images, Native AOT</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">.NET Standard 2.0 only matters now for a library that must serve both a .NET Framework 4.8 app and a .NET 10 app. There won&#8217;t be a new .NET Standard version.</p>



<p class="wp-block-paragraph">If a team asks why they should migrate, I&#8217;d say performance, Linux hosting costs and support for new C# versions. Web Forms and WCF server don&#8217;t exist in .NET 10, so those parts need a rewrite.</p>



<h3 class="wp-block-heading">Q3. What do LTS and STS mean, and which .NET version should you use in 2026? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> LTS (long-term support) releases get three years of free patches, STS (standard-term support) releases get two. In 2026, new projects should target .NET 10, the current LTS.</p>



<figure class="wp-block-table"><table><thead><tr><th>Version</th><th>Type</th><th>Released</th><th>End of support</th></tr></thead><tbody><tr><td>.NET 10</td><td>LTS</td><td>November 11, 2025</td><td>November 14, 2028</td></tr><tr><td>.NET 9</td><td>STS</td><td>November 12, 2024</td><td>November 10, 2026</td></tr><tr><td>.NET 8</td><td>LTS</td><td>November 14, 2023</td><td>November 10, 2026</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Here&#8217;s a detail that impresses interviewers: .NET 8 and .NET 9 both reach end of support on November 10, 2026. Even-numbered releases are LTS, and the last six months of any release are security-fixes only.</p>



<h3 class="wp-block-heading">Q4. Which .NET CLI commands should you know? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> <code>dotnet new</code>, <code>dotnet build</code>, <code>dotnet run</code>, <code>dotnet test</code>, <code>dotnet publish</code> and <code>dotnet add package</code>. In .NET 10 you can also run a single file with <code>dotnet run app.cs</code>.</p>



<p class="wp-block-paragraph">I used that single-file feature for the demos here. A <code>#:package</code> line at the top pulls a NuGet package, and <code>#:sdk Microsoft.NET.Sdk.Web</code> turns the file into a web app. For a full project setup, see how to <a href="https://aspdotnethelp.com/create-a-console-application-in-visual-studio-code/">create a console application in Visual Studio Code</a>.</p>



<h2 class="wp-block-heading">Runtime Interview Questions: CLR, JIT, GC and Native AOT (Junior to Mid)</h2>



<h3 class="wp-block-heading">Q5. What does the CLR do? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> The Common Language Runtime loads your compiled IL, JIT-compiles it to machine code, manages memory with the garbage collector, and enforces type safety and exception handling.</p>



<p class="wp-block-paragraph">Questions about value and reference types usually follow. Be ready to explain <a href="https://aspdotnethelp.com/boxing-and-unboxing-in-csharp/">boxing and unboxing in C#</a>, because it&#8217;s where the CLR&#8217;s two memory models meet.</p>



<h3 class="wp-block-heading">Q6. What&#8217;s the difference between JIT, ReadyToRun and Native AOT? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> JIT compiles IL to machine code at run time. ReadyToRun precompiles most code but keeps the JIT. Native AOT compiles everything ahead of time into one native executable with no JIT at all.</p>



<p class="wp-block-paragraph">Native AOT gives faster startup and lower memory, which is great for containers and serverless. The trade-off is no runtime reflection or dynamic code, so minimal APIs work (partially), but MVC controllers, Razor Pages, session state and Blazor Server don&#8217;t.</p>



<h3 class="wp-block-heading">Q7. How does the garbage collector work, and where does IDisposable fit? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> The GC frees managed memory in generations (0, 1 and 2, plus the large object heap). It doesn&#8217;t close files, sockets or database connections on time, so those types implement <code>IDisposable</code> and you wrap them in <code>using</code>.</p>



<p class="wp-block-paragraph">Interviewers love the follow-up about finalizers. My guide to <a href="https://aspdotnethelp.com/dispose-and-finalize-in-csharp/">Dispose and Finalize in C#</a> shows the full pattern and why you rarely write a finalizer yourself.</p>



<h3 class="wp-block-heading">Q8. What&#8217;s the difference between framework-dependent and self-contained deployment? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> A framework-dependent app needs the .NET runtime installed on the server and is small. A self-contained app ships its own runtime, so it&#8217;s bigger but runs on a machine with nothing installed.</p>



<p class="wp-block-paragraph">You pick it at publish time, for example <code>dotnet publish -c Release -r linux-x64 --self-contained</code>. Native AOT is a third option that&#8217;s always self-contained.</p>



<h2 class="wp-block-heading">Dependency Injection Interview Questions (Mid)</h2>



<h3 class="wp-block-heading">Q9. What is dependency injection in ASP.NET Core? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> A class asks for what it needs through its constructor, and the built-in container (<code>IServiceProvider</code>) creates and passes those objects in. You register services on <code>builder.Services</code> in Program.cs.</p>



<p class="wp-block-paragraph">It works best when a class depends on an abstraction, so you can swap the real thing for a fake in tests. That&#8217;s why every DI answer leads back to <a href="https://aspdotnethelp.com/interface-in-csharp/">interfaces in C#</a>.</p>



<p class="wp-block-paragraph">Constructor injection is the default, and C# 12 primary constructors make it shorter. If constructors are fuzzy, review <a href="https://aspdotnethelp.com/constructor-in-csharp/">constructors in C#</a> before the interview.</p>



<h3 class="wp-block-heading">Q10. What&#8217;s the difference between AddSingleton, AddScoped and AddTransient? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Singleton: one instance for the whole app. Scoped: one instance per scope, which in ASP.NET Core means per HTTP request. Transient: a new instance every time it&#8217;s requested.</p>



<p class="wp-block-paragraph">This is the most-asked ASP.NET Core question, so I proved it. The program below resolves each service twice inside two scopes, which is exactly what two HTTP requests look like to the container:</p>



<pre class="wp-block-code"><code>#:package Microsoft.Extensions.DependencyInjection@10.0.13
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddSingleton&lt;PricingRules&gt;();
services.AddScoped&lt;ShoppingCart&gt;();
services.AddTransient&lt;TaxCalculator&gt;();
using var root = services.BuildServiceProvider();

for (int request = 1; request &lt;= 2; request++)
{
    using var scope = root.CreateScope();   // = one HTTP request
    Console.WriteLine($&quot;Request {request}&quot;);
    Show&lt;PricingRules&gt;(scope.ServiceProvider);
    Show&lt;ShoppingCart&gt;(scope.ServiceProvider);
    Show&lt;TaxCalculator&gt;(scope.ServiceProvider);
}

static void Show&lt;T&gt;(IServiceProvider sp) where T : IHasId =&gt;
    Console.WriteLine($&quot;  {typeof(T).Name,-14} #{sp.GetRequiredService&lt;T&gt;().Id}  #{sp.GetRequiredService&lt;T&gt;().Id}&quot;);

interface IHasId { int Id { get; } }
class Counted&lt;T&gt; : IHasId { static int count; public int Id { get; } = ++count; }
class PricingRules : Counted&lt;PricingRules&gt;;
class ShoppingCart : Counted&lt;ShoppingCart&gt;;
class TaxCalculator : Counted&lt;TaxCalculator&gt;;</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>Request 1
  PricingRules   #1  #1
  ShoppingCart   #1  #1
  TaxCalculator  #1  #2
Request 2
  PricingRules   #1  #1
  ShoppingCart   #2  #2
  TaxCalculator  #3  #4</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-di-lifetimes-singleton-scoped-transient-output.png" alt="VS Code running Lifetimes.cs: .NET dependency injection lifetimes, singleton, scoped and transient instance numbers across two scopes" class="wp-image-1948" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-di-lifetimes-singleton-scoped-transient-output.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-di-lifetimes-singleton-scoped-transient-output-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-di-lifetimes-singleton-scoped-transient-output-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-di-lifetimes-singleton-scoped-transient-output-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">Singleton keeps #1, scoped changes per request, transient changes on every resolve</figcaption></figure>
</div>


<p class="wp-block-paragraph"><code>PricingRules</code> stays #1 everywhere. <code>ShoppingCart</code> is #1 twice in request 1 and #2 twice in request 2. <code>TaxCalculator</code> gets a new number on every call, #1 through #4.</p>



<figure class="wp-block-table"><table><thead><tr><th>Lifetime</th><th>One instance per</th><th>Typical example</th></tr></thead><tbody><tr><td>Singleton</td><td>application</td><td>configuration cache, <code>HttpClient</code> factory, <code>HybridCache</code></td></tr><tr><td>Scoped</td><td>HTTP request (scope)</td><td>EF Core <code>DbContext</code>, unit of work, current user</td></tr><tr><td>Transient</td><td>resolve</td><td>lightweight, stateless helpers</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">Q11. What is a captive dependency, and how do you catch it? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> A captive dependency is a longer-lived service holding a shorter-lived one, usually a singleton that takes a scoped <code>DbContext</code> in its constructor. The scoped object then lives forever and is shared across requests.</p>



<pre class="wp-block-code"><code>#:package Microsoft.Extensions.DependencyInjection@10.0.13
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddScoped&lt;OrderDbContext&gt;();
services.AddSingleton&lt;PriceCache&gt;();    // singleton asks for scoped

using var loose = services.BuildServiceProvider();
using var s1 = loose.CreateScope();
using var s2 = loose.CreateScope();
int a = s1.ServiceProvider.GetRequiredService&lt;PriceCache&gt;().Db.Id;
int b = s2.ServiceProvider.GetRequiredService&lt;PriceCache&gt;().Db.Id;
Console.WriteLine($&quot;No validation: scope 1 DbContext #{a}, scope 2 #{b}&quot;);

using var strict = services.BuildServiceProvider(validateScopes: true);
try { strict.GetRequiredService&lt;PriceCache&gt;(); }
catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); }

class OrderDbContext { static int count; public int Id { get; } = ++count; }
class PriceCache(OrderDbContext db) { public OrderDbContext Db =&gt; db; }</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>No validation: scope 1 DbContext #1, scope 2 #1
Cannot consume scoped service &#x27;OrderDbContext&#x27; from singleton &#x27;PriceCache&#x27;.</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-captive-dependency-validatescopes-exception.png" alt="VS Code running Captive.cs: captive dependency in .NET DI, the same DbContext in two scopes and the ValidateScopes InvalidOperationException" class="wp-image-1946" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-captive-dependency-validatescopes-exception.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-captive-dependency-validatescopes-exception-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-captive-dependency-validatescopes-exception-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-captive-dependency-validatescopes-exception-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">Without validation both scopes share DbContext #1; ValidateScopes throws instead</figcaption></figure>
</div>


<p class="wp-block-paragraph">Without validation, both scopes got DbContext #1, which is a thread-safety bug waiting to happen. With <code>validateScopes: true</code>, the container threw &#8220;Cannot consume scoped service &#8216;OrderDbContext&#8217; from singleton &#8216;PriceCache&#8217;.&#8221;</p>



<p class="wp-block-paragraph">ASP.NET Core turns scope validation on in the Development environment, so you&#8217;ll usually see this at startup. The fix is to inject <code>IServiceScopeFactory</code> into the singleton and create a scope when you need the DbContext.</p>



<h3 class="wp-block-heading">Q12. What are keyed services? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Added in .NET 8, keyed services let you register several implementations of one interface under different keys, then pick one with <code>[FromKeyedServices("key")]</code>.</p>



<p class="wp-block-paragraph">For example, <code>AddKeyedSingleton&lt;IShippingRates, UpsRates&gt;("ups")</code> and another for &#8220;fedex&#8221;. Before .NET 8 you&#8217;d write a factory or inject <code>IEnumerable&lt;T&gt;</code> and filter it.</p>



<h2 class="wp-block-heading">Middleware Pipeline Interview Questions (Mid)</h2>



<h3 class="wp-block-heading">Q13. What is middleware, and what&#8217;s the difference between Use, Run and Map? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Middleware is a component in the request pipeline that can handle a request, change it, and call the next one. <code>Use</code> calls <code>next</code>, <code>Run</code> ends the pipeline, and <code>Map</code> branches it by path.</p>



<p class="wp-block-paragraph">Each piece is a <code>RequestDelegate</code>, so knowing <a href="https://aspdotnethelp.com/delegates-in-csharp/">delegates in C#</a> makes the whole model click.</p>



<h3 class="wp-block-heading">Q14. Does middleware order matter? Prove it. <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Yes. Middleware runs top to bottom on the way in and bottom to top on the way out, so the first one registered sees the request first and the response last.</p>



<p class="wp-block-paragraph">I ran a real minimal API in memory with <code>TestServer</code>, no port needed, and logged each step:</p>



<pre class="wp-block-code"><code>#:sdk Microsoft.NET.Sdk.Web
#:package Microsoft.AspNetCore.TestHost@10.0.13
using Microsoft.AspNetCore.TestHost;

var builder = WebApplication.CreateBuilder();
builder.WebHost.UseTestServer();      // in-memory server, no port
builder.Logging.ClearProviders();
var app = builder.Build();

app.Use(Step(&quot;1 RequestTimer&quot;));
app.Use(Step(&quot;2 CorrelationId&quot;));
app.MapGet(&quot;/orders/{id}&quot;, (int id) =&gt;
{
    Console.WriteLine(&quot;3 Endpoint runs&quot;);
    return $&quot;Order {id} shipped to Austin, TX&quot;;
});

await app.StartAsync();
var body = await app.GetTestClient().GetStringAsync(&quot;/orders/1042&quot;);
Console.WriteLine($&quot;Response: {body}&quot;);
await app.StopAsync();

static Func&lt;HttpContext, RequestDelegate, Task&gt; Step(string name) =&gt; async (ctx, next) =&gt;
{
    Console.WriteLine($&quot;{name,-16}-&gt; in&quot;);
    await next(ctx);
    Console.WriteLine($&quot;{name,-16}&lt;- out&quot;);
};</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>1 RequestTimer  -&gt; in
2 CorrelationId -&gt; in
3 Endpoint runs
2 CorrelationId &lt;- out
1 RequestTimer  &lt;- out
Response: Order 1042 shipped to Austin, TX</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-middleware-order-testserver-output.png" alt="VS Code running Pipeline.cs: ASP.NET Core middleware order with app.Use and a minimal API endpoint, in and out messages from TestServer" class="wp-image-1945" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-middleware-order-testserver-output.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-middleware-order-testserver-output-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-middleware-order-testserver-output-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/aspnet-core-middleware-order-testserver-output-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">RequestTimer runs first in and last out; the endpoint runs in the middle</figcaption></figure>
</div>


<p class="wp-block-paragraph">The output nests like parentheses: 1 in, 2 in, endpoint, 2 out, 1 out. That&#8217;s why the exception handler goes first, so it can catch anything thrown later.</p>



<p class="wp-block-paragraph">Microsoft&#8217;s recommended order is: exception handler and HSTS, HTTPS redirection, static files, routing, CORS, authentication, authorization, then your custom middleware and endpoints. <code>WebApplication</code> adds <code>UseRouting</code> for you when you don&#8217;t call it.</p>



<h3 class="wp-block-heading">Q15. How do you handle exceptions globally in ASP.NET Core? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Call <code>app.UseExceptionHandler()</code> early in the pipeline, register <code>AddProblemDetails()</code> so errors come back as RFC 7807 JSON, and optionally add an <code>IExceptionHandler</code> class for custom logic.</p>



<p class="wp-block-paragraph">Inside the endpoint itself you still use <a href="https://aspdotnethelp.com/try-catch-in-csharp/">try-catch in C#</a> for errors you can actually recover from, like a retry. Don&#8217;t wrap every action in try-catch just to log; that&#8217;s what the handler is for.</p>



<h2 class="wp-block-heading">Configuration and Options Pattern Interview Questions (Mid)</h2>



<h3 class="wp-block-heading">Q16. Where does ASP.NET Core read configuration from, and in what order? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> <code>WebApplication.CreateBuilder</code> loads appsettings.json, then appsettings.{Environment}.json, then user secrets (in Development), then environment variables, then command-line arguments. Later sources win.</p>



<p class="wp-block-paragraph">So <code>Store__City=Denver</code> as an environment variable overrides the JSON file. That&#8217;s how Azure App Service settings replace values without changing the deployed files.</p>



<h3 class="wp-block-heading">Q17. What&#8217;s the difference between appsettings.json and secrets.json? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> appsettings.json is committed with the code. secrets.json (user secrets) lives in your user profile, never in the repo, and only loads in Development.</p>



<p class="wp-block-paragraph">In production, secrets belong in Azure Key Vault or environment variables, not in either file. Interviewers want to hear that you&#8217;ve never pushed a connection string to GitHub.</p>



<h3 class="wp-block-heading">Q18. How does the options pattern work, and IOptions vs IOptionsSnapshot vs IOptionsMonitor? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> You bind a configuration section to a plain class and inject <code>IOptions&lt;T&gt;</code>. <code>IOptions</code> reads once (singleton), <code>IOptionsSnapshot</code> re-reads per request (scoped), and <code>IOptionsMonitor</code> gets change notifications (singleton).</p>



<p class="wp-block-paragraph">Here I bind a &#8220;Store&#8221; section from in-memory configuration, which is just a <a href="https://aspdotnethelp.com/dictionary-in-csharp/">C# dictionary</a> of keys like <code>Store:City</code>:</p>



<pre class="wp-block-code"><code>#:package Microsoft.Extensions.Options.ConfigurationExtensions@10.0.13
#:package Microsoft.Extensions.DependencyInjection@10.0.13
using System.Globalization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

CultureInfo.CurrentCulture = new CultureInfo(&quot;en-US&quot;);
// The same keys you&#x27;d put in appsettings.json under &quot;Store&quot;
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary&lt;string, string?&gt;
{
    [&quot;Store:City&quot;] = &quot;Seattle&quot;,
    [&quot;Store:SalesTaxRate&quot;] = &quot;0.1035&quot;,
    [&quot;Store:FreeShippingOver&quot;] = &quot;75&quot;
}).Build();

var services = new ServiceCollection();
services.AddOptions&lt;StoreOptions&gt;().Bind(config.GetSection(&quot;Store&quot;));
using var sp = services.BuildServiceProvider();

StoreOptions store = sp.GetRequiredService&lt;IOptions&lt;StoreOptions&gt;&gt;().Value;
Console.WriteLine($&quot;City: {store.City}&quot;);
Console.WriteLine($&quot;Sales tax: {store.SalesTaxRate:P2}&quot;);
Console.WriteLine($&quot;Free shipping over: {store.FreeShippingOver:C0}&quot;);

public class StoreOptions
{
    public string City { get; set; } = &quot;&quot;;
    public decimal SalesTaxRate { get; set; }
    public decimal FreeShippingOver { get; set; }
}</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>City: Seattle
Sales tax: 10.35%
Free shipping over: $75</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-ioptions-bind-in-memory-configuration.png" alt="VS Code running Options.cs: .NET options pattern binding a Store section to StoreOptions and reading it with IOptions" class="wp-image-1950" srcset="https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-ioptions-bind-in-memory-configuration.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-ioptions-bind-in-memory-configuration-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-ioptions-bind-in-memory-configuration-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2026/10/dotnet-ioptions-bind-in-memory-configuration-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">The binder converted &#8220;0.1035&#8221; and &#8220;75&#8221; into decimals on StoreOptions</figcaption></figure>
</div>


<p class="wp-block-paragraph">The binder turned the string &#8220;0.1035&#8221; into a decimal, shown as 10.35%. Add <code>.ValidateDataAnnotations().ValidateOnStart()</code> and a bad value fails at startup instead of on the first request.</p>



<h2 class="wp-block-heading">Program.cs, Hosting and Kestrel Interview Questions (Mid)</h2>



<h3 class="wp-block-heading">Q19. What happened to Startup.cs, ConfigureServices and Configure? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Since .NET 6, the templates use minimal hosting: one Program.cs where <code>builder.Services</code> replaces <code>ConfigureServices</code> and the code after <code>builder.Build()</code> replaces <code>Configure</code>.</p>



<pre class="wp-block-code"><code>var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();        // was ConfigureServices

var app = builder.Build();
app.UseExceptionHandler();                    // was Configure
app.MapGet(&quot;/health&quot;, () =&gt; &quot;OK&quot;);
app.Run();</code></pre>



<p class="wp-block-paragraph">Startup.cs still works if you call it from the generic host, so older apps don&#8217;t break. But if an interviewer asks about it, show that you know the Program.cs version.</p>



<h3 class="wp-block-heading">Q20. What is the Generic Host, and what&#8217;s a Worker Service? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> The Generic Host (<code>IHost</code>) wires up DI, configuration, logging and app lifetime for any .NET app. A Worker Service is a host with no web server that runs <code>BackgroundService</code> classes.</p>



<p class="wp-block-paragraph"><code>BackgroundService</code> is an <a href="https://aspdotnethelp.com/abstract-class-in-csharp/">abstract class in C#</a> where you override <code>ExecuteAsync</code>. It&#8217;s a good fit for queue processors or a nightly job that will <a href="https://aspdotnethelp.com/send-emails-with-attachments/">send emails with attachments</a>.</p>



<h3 class="wp-block-heading">Q21. What is Kestrel, and do you still need IIS? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Kestrel is ASP.NET Core&#8217;s built-in, cross-platform web server. On Windows, IIS can host the app in-process through the ASP.NET Core Module; on Linux you usually put Nginx, YARP or a cloud load balancer in front.</p>



<p class="wp-block-paragraph">Kestrel is fast enough to face the internet directly, but a reverse proxy still helps with TLS termination, multiple sites on one IP and request filtering.</p>



<h3 class="wp-block-heading">Q22. How do you deploy ASP.NET Core to containers and Azure App Service? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> For containers, <code>dotnet publish /t:PublishContainer</code> builds an image without a Dockerfile, and .NET 8+ images listen on port 8080 as a non-root user. For Azure App Service, publish from Visual Studio, the CLI or a GitHub Actions workflow.</p>



<p class="wp-block-paragraph">Senior candidates should mention health checks (<code>MapHealthChecks</code>), deployment slots for zero-downtime swaps, and keeping secrets in Key Vault references.</p>



<h2 class="wp-block-heading">Minimal APIs vs Controllers: Web API Interview Questions (Mid)</h2>



<h3 class="wp-block-heading">Q23. When would you use minimal APIs instead of controllers? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Minimal APIs are lighter and work with Native AOT, so they&#8217;re great for microservices and new APIs. Controllers still make sense for large apps that rely on filters, model binding conventions and an existing MVC codebase.</p>



<p class="wp-block-paragraph">Both return JSON through System.Text.Json. If you&#8217;ve ever fought escaped quotes in output, my post on how to <a href="https://aspdotnethelp.com/convert-object-to-json-without-escape-characters-in-csharp/">convert an object to JSON without escape characters</a> explains the encoder settings.</p>



<pre class="wp-block-code"><code>var orders = app.MapGroup(&quot;/api/orders&quot;).RequireAuthorization();
orders.MapGet(&quot;/{id:int}&quot;, async (int id, OrderDbContext db) =&gt;
    await db.Orders.FindAsync(id) is Order o ? Results.Ok(o) : Results.NotFound());</code></pre>



<h3 class="wp-block-heading">Q24. How do you document an API with OpenAPI (Swagger) in .NET 10? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Call <code>builder.Services.AddOpenApi()</code> and <code>app.MapOpenApi()</code>. Since .NET 9 the templates use Microsoft.AspNetCore.OpenApi instead of Swashbuckle, and in .NET 10 the generated document is OpenAPI 3.1 by default.</p>



<p class="wp-block-paragraph">You can still add Swagger UI or Scalar for a browsable page. .NET 10 also added built-in minimal API validation with <code>builder.Services.AddValidation()</code>.</p>



<h3 class="wp-block-heading">Q25. What are Razor Pages, Blazor and SignalR used for? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Razor Pages is page-focused server rendering, Blazor builds interactive UI in C# (server or WebAssembly), and SignalR pushes real-time messages to browsers, like live order tracking.</p>



<p class="wp-block-paragraph">SignalR&#8217;s hub methods feel a lot like <a href="https://aspdotnethelp.com/events-in-csharp/">events in C#</a> that cross the network. For a quick server-to-browser stream, .NET 10 also has <code>TypedResults.ServerSentEvents</code>.</p>



<h2 class="wp-block-heading">Authentication and Authorization Interview Questions (Mid to Senior)</h2>



<h3 class="wp-block-heading">Q26. Authentication vs authorization: how does JWT bearer authentication work? <em>(Mid)</em></h3>



<p class="wp-block-paragraph">Authentication answers &#8220;who are you?&#8221; and builds the <code>ClaimsPrincipal</code>; authorization answers &#8220;what may you do?&#8221; with roles, claims or policies. That&#8217;s why <code>UseAuthentication</code> comes before <code>UseAuthorization</code>.</p>



<p class="wp-block-paragraph"><strong>Short answer:</strong> The client sends <code>Authorization: Bearer &lt;token&gt;</code>. <code>AddJwtBearer</code> validates the signature, issuer, audience and expiry, then turns the claims into <code>HttpContext.User</code>.</p>



<p class="wp-block-paragraph">Keep access tokens short-lived and never put secrets or personal data in the payload, because anyone can base64-decode it. Signing isn&#8217;t encryption.</p>



<h3 class="wp-block-heading">Q27. How do you protect an API with Microsoft Entra ID? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Register the API in Microsoft Entra ID (formerly Azure AD), then use Microsoft.Identity.Web: <code>AddAuthentication().AddMicrosoftIdentityWebApi(builder.Configuration)</code> with an &#8220;AzureAd&#8221; config section, and require scopes or app roles per endpoint.</p>



<p class="wp-block-paragraph">For apps with their own sign-in, ASP.NET Core Identity in .NET 10 now supports passkeys. If you&#8217;ve built a custom flow like an <a href="https://aspdotnethelp.com/generate-otp-asp-net/">OTP in ASP.NET</a>, be ready to explain why you&#8217;d move to a standard provider.</p>



<h3 class="wp-block-heading">Q28. Which security measures do you add to every ASP.NET Core app? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> HTTPS with HSTS, a strict CORS policy, antiforgery for cookie-based forms, rate limiting (<code>AddRateLimiter</code>), secrets in Key Vault, and authorization on by default with a fallback policy.</p>



<h2 class="wp-block-heading">Entity Framework Core Interview Questions (Mid to Senior)</h2>



<h3 class="wp-block-heading">Q29. What is EF Core, and what does change tracking do? <em>(Junior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> EF Core is Microsoft&#8217;s ORM: you query with LINQ and save with <code>SaveChangesAsync</code>. Change tracking remembers loaded entities so it can generate UPDATE statements; <code>AsNoTracking()</code> skips that for read-only queries.</p>



<p class="wp-block-paragraph">Queries come back as a <a href="https://aspdotnethelp.com/list-in-csharp/">List in C#</a> or a single entity. Remember that <code>AddDbContext</code> registers the context as scoped, which ties back to the lifetime question above.</p>



<h3 class="wp-block-heading">Q30. What is the N+1 problem, and how do you fix it? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> N+1 is one query for a list plus one extra query per row, usually from lazy loading inside a loop. Fix it with <code>Include</code>, a <code>Select</code> projection to a DTO, or <code>AsSplitQuery()</code> for big graphs.</p>



<pre class="wp-block-code"><code>// 1 query instead of 1 + N
var report = await db.Customers
    .Where(c =&gt; c.State == &quot;CA&quot;)
    .Select(c =&gt; new { c.Name, OrderCount = c.Orders.Count })
    .AsNoTracking()
    .ToListAsync();</code></pre>



<h3 class="wp-block-heading">Q31. How do you apply EF Core migrations in production? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Create them with <code>dotnet ef migrations add</code>, then in production apply a reviewed SQL script (<code>dotnet ef migrations script --idempotent</code>) or a migration bundle from your pipeline. Avoid calling <code>Migrate()</code> at app startup on multiple instances.</p>



<h2 class="wp-block-heading">Async/Await and Logging Interview Questions (Mid)</h2>



<h3 class="wp-block-heading">Q32. How does async/await help an ASP.NET Core app? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> While an <code>await</code> waits on I/O, the thread goes back to the pool to serve other requests. It doesn&#8217;t make one request faster, but it lets the server handle many more at once.</p>



<p class="wp-block-paragraph">You can still make independent calls overlap. I called a fake 1-second tax API three times, first one after another, then with <code>Task.WhenAll</code>:</p>



<pre class="wp-block-code"><code>using System.Diagnostics;

var sw = Stopwatch.StartNew();
await GetRateAsync(&quot;Texas&quot;); await GetRateAsync(&quot;Ohio&quot;); await GetRateAsync(&quot;Utah&quot;);
Console.WriteLine($&quot;One after another: {sw.Elapsed.TotalSeconds:F1} s&quot;);

sw.Restart();
decimal[] rates = await Task.WhenAll(GetRateAsync(&quot;Texas&quot;), GetRateAsync(&quot;Ohio&quot;), GetRateAsync(&quot;Utah&quot;));
Console.WriteLine($&quot;Task.WhenAll:      {sw.Elapsed.TotalSeconds:F1} s -&gt; {string.Join(&quot;, &quot;, rates)}&quot;);

// Pretend each call is a 1-second HTTP request to a sales tax API
static async Task&lt;decimal&gt; GetRateAsync(string state)
{
    await Task.Delay(1000);   // frees the thread while it waits, unlike Thread.Sleep
    return state switch { &quot;Texas&quot; =&gt; 6.25m, &quot;Ohio&quot; =&gt; 5.75m, _ =&gt; 6.10m };
}</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>One after another: 3.0 s
Task.WhenAll:      1.0 s -&gt; 6.25, 5.75, 6.10</code></pre>



<p class="wp-block-paragraph">Sequential took 3.0 seconds and <code>Task.WhenAll</code> took 1.0. Never use <code>.Result</code> or <code>.Wait()</code> on a task in a request; it blocks a thread and can starve the pool.</p>



<h3 class="wp-block-heading">Q33. What&#8217;s the difference between concurrency and parallelism, and what are async streams? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Concurrency is juggling several tasks that overlap in time (like the awaits above); parallelism is running work on several CPU cores at once (<code>Parallel.ForEachAsync</code>). Async streams (<code>IAsyncEnumerable&lt;T&gt;</code> with <code>await foreach</code>) return items as they arrive.</p>



<h3 class="wp-block-heading">Q34. How does logging work with ILogger, and where does Serilog fit? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> You inject <code>ILogger&lt;T&gt;</code> and write structured messages with named placeholders. Providers (console, Application Insights, Serilog) decide where they go, and the minimum level filters the noise.</p>



<pre class="wp-block-code"><code>#:package Microsoft.Extensions.Logging.Console@10.0.13
using Microsoft.Extensions.Logging;

using var factory = LoggerFactory.Create(b =&gt; b
    .AddSimpleConsole(o =&gt; o.SingleLine = true)
    .SetMinimumLevel(LogLevel.Information));
ILogger logger = factory.CreateLogger(&quot;Orders&quot;);

logger.LogDebug(&quot;Cart contents: {Items}&quot;, 3);    // below the minimum level, so it&#x27;s skipped
logger.LogInformation(&quot;Order {OrderId} placed by {Customer}&quot;, 1042, &quot;Jenna Moore&quot;);
logger.LogWarning(&quot;Inventory low for {Sku} in {City}&quot;, &quot;SKU-778&quot;, &quot;Denver&quot;);
logger.LogError(new TimeoutException(&quot;Payment gateway timed out&quot;), &quot;Payment failed for order {OrderId}&quot;, 1043);</code></pre>



<p class="wp-block-paragraph"><strong>Output:</strong></p>



<pre class="wp-block-code"><code>info: Orders[0] Order 1042 placed by Jenna Moore
warn: Orders[0] Inventory low for SKU-778 in Denver
fail: Orders[0] Payment failed for order 1043 System.TimeoutException: Payment gateway timed out</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="1084" height="952" src="https://aspdotnethelp.com/wp-content/uploads/2023/05/dotnet-ilogger-structured-logging-console-output.png" alt="VS Code running Logging.cs: .NET ILogger structured logging to the console with info, warn and fail levels" class="wp-image-2375" srcset="https://aspdotnethelp.com/wp-content/uploads/2023/05/dotnet-ilogger-structured-logging-console-output.png 1084w, https://aspdotnethelp.com/wp-content/uploads/2023/05/dotnet-ilogger-structured-logging-console-output-300x263.png 300w, https://aspdotnethelp.com/wp-content/uploads/2023/05/dotnet-ilogger-structured-logging-console-output-1024x899.png 1024w, https://aspdotnethelp.com/wp-content/uploads/2023/05/dotnet-ilogger-structured-logging-console-output-768x674.png 768w" sizes="auto, (max-width: 1084px) 100vw, 1084px" /><figcaption class="wp-element-caption">LogDebug is filtered out; info, warn and fail lines reach the console</figcaption></figure>
</div>


<p class="wp-block-paragraph">The Debug line never printed because the minimum level was Information. Use <code>{OrderId}</code> placeholders, not string interpolation, so a sink like Serilog or Application Insights can store OrderId as a searchable field.</p>



<p class="wp-block-paragraph">Serilog plugs in with <code>builder.Host.UseSerilog()</code>. Older apps often used log4net.</p>



<h2 class="wp-block-heading">Performance and Caching Interview Questions (Senior)</h2>



<h3 class="wp-block-heading">Q35. IMemoryCache, IDistributedCache, HybridCache or output caching: which one? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Use <code>IMemoryCache</code> for one server, <code>IDistributedCache</code> (Redis) to share across servers, <code>HybridCache</code> for both layers plus stampede protection, and output caching to store whole HTTP responses.</p>



<figure class="wp-block-table"><table><thead><tr><th>Cache</th><th>Stores</th><th>Good for</th></tr></thead><tbody><tr><td><code>IMemoryCache</code></td><td>objects in this process</td><td>single instance, small data</td></tr><tr><td><code>IDistributedCache</code></td><td>bytes in Redis or SQL Server</td><td>web farms, sessions</td></tr><tr><td><code>HybridCache</code></td><td>memory + optional distributed</td><td>most new code (<code>GetOrCreateAsync</code>)</td></tr><tr><td>Output caching</td><td>full HTTP responses</td><td>GET endpoints (<code>CacheOutput()</code>)</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><code>HybridCache</code> lives in the Microsoft.Extensions.Caching.Hybrid package and you register it with <code>AddHybridCache()</code>. Only one caller per key runs the factory, so a cold cache doesn&#8217;t hammer your database.</p>



<h3 class="wp-block-heading">Q36. How would you speed up a slow ASP.NET Core API? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Measure first (Application Insights, dotnet-counters), then fix the biggest cost: usually database round trips, missing indexes or N+1 queries, then caching, response compression and async I/O.</p>



<p class="wp-block-paragraph">Smaller wins come from avoiding allocations in hot paths, such as knowing the <a href="https://aspdotnethelp.com/difference-between-string-and-stringbuilder-in-csharp/">difference between string and StringBuilder in C#</a>, and using <code>IHttpClientFactory</code> instead of creating <code>HttpClient</code> per call.</p>



<h2 class="wp-block-heading">Testing Interview Questions: xUnit and WebApplicationFactory (Mid)</h2>



<h3 class="wp-block-heading">Q37. How do you unit test and integration test an ASP.NET Core app? <em>(Mid)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Unit test classes with xUnit (or NUnit/MSTest) and fakes for their interfaces. Integration test the whole pipeline with <code>WebApplicationFactory&lt;Program&gt;</code> from Microsoft.AspNetCore.Mvc.Testing, which runs the app on an in-memory <code>TestServer</code>.</p>



<pre class="wp-block-code"><code>public class OrdersApiTests(WebApplicationFactory&lt;Program&gt; factory)
    : IClassFixture&lt;WebApplicationFactory&lt;Program&gt;&gt;
{
    [Fact]
    public async Task Get_order_returns_200()
    {
        var client = factory.CreateClient();
        var response = await client.GetAsync(&quot;/orders/1042&quot;);
        response.EnsureSuccessStatusCode();
    }
}</code></pre>



<p class="wp-block-paragraph">With top-level statements, the test project needs to see <code>Program</code>, so many teams add <code>public partial class Program { }</code>; that&#8217;s a <a href="https://aspdotnethelp.com/partial-class-in-csharp/">partial class in C#</a>. The middleware demo above used the same <code>TestServer</code> underneath.</p>



<h2 class="wp-block-heading">Behavioral .NET Interview Questions (All Levels)</h2>



<p class="wp-block-paragraph">US companies spend real time on these, often one full round. Answer with the STAR format: situation, task, action, result, and give numbers when you have them.</p>



<h3 class="wp-block-heading">Q38. Tell me about a production bug you fixed. <em>(All levels)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Pick a real one with a clear cause, like a captive DbContext causing random errors under load. Explain how you found it (logs, traces), the fix, and what you changed so it can&#8217;t happen again.</p>



<h3 class="wp-block-heading">Q39. How would you migrate a .NET Framework app to .NET 10? <em>(Senior)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Inventory dependencies, move class libraries first (to .NET Standard 2.0 or multi-target), then the web layer, one slice at a time behind a reverse proxy (the strangler pattern) so both apps run side by side.</p>



<h3 class="wp-block-heading">Q40. How do you disagree with a teammate&#8217;s design? <em>(All levels)</em></h3>



<p class="wp-block-paragraph"><strong>Short answer:</strong> Ask questions first, bring data like a quick benchmark or a spike, agree on the trade-off, and once the team decides, commit to it. Interviewers look for respect and ownership, not winning.</p>



<h2 class="wp-block-heading">Which .NET Core Interview Questions to Study for Your Level?</h2>



<figure class="wp-block-table"><table><thead><tr><th>Level</th><th>Focus on</th><th>Questions</th></tr></thead><tbody><tr><td>Junior (0-2 years)</td><td>platform, CLI, DI basics, Program.cs</td><td>Q1-Q4, Q9, Q13, Q17, Q19</td></tr><tr><td>Mid (2-5 years)</td><td>lifetimes, middleware order, options, EF Core, async</td><td>Q10, Q14-Q18, Q23-Q24, Q29, Q32</td></tr><tr><td>Senior (5+ years)</td><td>captive dependencies, N+1, caching, security, migration</td><td>Q11, Q27-Q28, Q30-Q31, Q35-Q36, Q39</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">My advice: run the six demos above yourself. Saying &#8220;I&#8217;ve seen ValidateScopes throw on this exact case&#8221; lands far better than reciting a definition.</p>



<p class="wp-block-paragraph">For the C# side of the same interview loop, these pair well with this guide:</p>



<ul class="wp-block-list">
<li><a href="https://aspdotnethelp.com/csharp-interview-questions-and-answers-for-experienced-professionals/">C# interview questions for experienced developers</a></li>

<li><a href="https://aspdotnethelp.com/csharp-interview-questions-and-answers-for-freshers/">C# interview questions for freshers</a></li>

<li><a href="https://aspdotnethelp.com/oops-concepts-in-csharp/">OOP concepts in C# with examples</a></li>

<li><a href="https://aspdotnethelp.com/method-overriding-in-csharp/">Method overriding in C#</a></li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-nq0000001" class="rank-math-list-item">
<h3 class="rank-math-question ">Is .NET Core still used in 2026?</h3>
<div class="rank-math-answer ">

<p>Yes, under the name .NET. Every version since .NET 5 is the continuation of .NET Core, and .NET 10 is the current LTS release, supported until November 14, 2028.</p>

</div>
</div>
<div id="faq-question-nq0000002" class="rank-math-list-item">
<h3 class="rank-math-question ">Which .NET version should I learn for interviews?</h3>
<div class="rank-math-answer ">

<p>Learn .NET 10 with C# 14. It&#8217;s the LTS release most US teams are moving to, and .NET 8 and .NET 9 both leave support on November 10, 2026.</p>

</div>
</div>
<div id="faq-question-nq0000003" class="rank-math-list-item">
<h3 class="rank-math-question ">Are Startup.cs questions still asked?</h3>
<div class="rank-math-answer ">

<p>Sometimes, for older codebases. Explain that <code>ConfigureServices</code> became <code>builder.Services</code> and <code>Configure</code> became the code after <code>builder.Build()</code> in Program.cs.</p>

</div>
</div>
<div id="faq-question-nq0000004" class="rank-math-list-item">
<h3 class="rank-math-question ">What&#8217;s the most common ASP.NET Core interview question?</h3>
<div class="rank-math-answer ">

<p>The difference between <code>AddSingleton</code>, <code>AddScoped</code> and <code>AddTransient</code>, usually followed by the captive dependency problem. The official <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/service-lifetimes" target="_blank" rel="noopener">service lifetimes guide on Microsoft Learn</a> is worth reading the night before.</p>

</div>
</div>
<div id="faq-question-nq0000005" class="rank-math-list-item">
<h3 class="rank-math-question ">Do I need to know Azure for a .NET job in the US?</h3>
<div class="rank-math-answer ">

<p>For most backend roles, yes at a basic level: App Service, Key Vault, Microsoft Entra ID and Application Insights come up often, along with containers.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img alt='Bijay Kumar' src='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=100&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/18a79d27129a98c6530098c50aef09aa901fced58315025237441af82a0fa179?s=200&#038;d=mm&#038;r=g 2x' class='avatar avatar-100 photo' height='100' width='100' itemprop="image"/></div><div class="saboxplugin-authorname"><a href="https://aspdotnethelp.com/bijay-kumar/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Bijay Kumar is a renowned software engineer, accomplished author, and distinguished <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000972" rel="nofollow noopener" target="_blank">Microsoft Most Valuable Professional</a> (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. <a href="https://aspdotnethelp.com/about/" rel="nofollow noopener">Read more&#8230;</a></p>
</div></div><div class="saboxplugin-web "><a href="https://aspdotnethelp.com" target="_self">aspdotnethelp.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials "><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-linkedin" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 448 512"><path fill="currentColor" d="M100.3 480H7.4V180.9h92.9V480zM53.8 140.1C24.1 140.1 0 115.5 0 85.8 0 56.1 24.1 32 53.8 32c29.7 0 53.8 24.1 53.8 53.8 0 29.7-24.1 54.3-53.8 54.3zM448 480h-92.7V334.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V480h-92.8V180.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V480z"></path></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-facebook" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 264 512"><path fill="currentColor" d="M76.7 512V283H0v-91h76.7v-71.7C76.7 42.4 124.3 0 193.8 0c33.3 0 61.9 2.5 70.2 3.6V85h-48.2c-37.8 0-45.1 18-45.1 44.3V192H256l-11.7 91h-73.6v229"></path></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/c/enjoysharepoint?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-youtube" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512"><path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-grey"><svg aria-hidden="true" class="sab-twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 30 30"><path d="M26.37,26l-8.795-12.822l0.015,0.012L25.52,4h-2.65l-6.46,7.48L11.28,4H4.33l8.211,11.971L12.54,15.97L3.88,26h2.65 l7.182-8.322L19.42,26H26.37z M10.23,6l12.34,18h-2.1L8.12,6H10.23z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: aspdotnethelp.com @ 2026-11-03 15:25:35 by W3 Total Cache
-->