How to Migrate from .NET Framework to .NET 10 (formerly .NET Core)

Type: Software Reference Confidence: 0.93 Sources: 9 Verified: 2026-02-23 Freshness: monthly

TL;DR

Constraints

Quick Reference

.NET Framework Pattern.NET 10 EquivalentNotes
System.Web.HttpContextMicrosoft.AspNetCore.Http.HttpContextCompletely different API -- use System.Web adapters for incremental migration [src5]
Web.config (XML)appsettings.json + Options patternUse IOptions<T> / IConfiguration for strongly-typed settings [src6]
Global.asax / Application_StartProgram.cs / WebApplication.CreateBuilder()Minimal hosting model in .NET 6+; Startup.cs optional [src3]
HTTP Modules (IHttpModule)Middleware (IMiddleware / convention)Register in app.UseMiddleware<T>(); order matters [src6]
HTTP Handlers (IHttpHandler)Middleware or MapGet/MapPost endpointsUse minimal APIs or controller endpoints [src6]
packages.configPackageReference in .csprojMigrate via VS right-click > "Migrate packages.config to PackageReference" [src1]
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion><TargetFramework>net10.0</TargetFramework>SDK-style project format required [src1]
WCF (server-side)gRPC, CoreWCF 2.x, or REST APICoreWCF 2.x supports .NET 8/10; gRPC recommended for new services [src7]
Entity Framework 6Entity Framework Core 10Different API surface; migrations not directly compatible [src2]
System.Web.Mvc.ControllerMicrosoft.AspNetCore.Mvc.ControllerNamespace swap; action filters and model binding differ [src3]
FormsAuthenticationASP.NET Core Identity / Cookie AuthUse AddAuthentication().AddCookie(); passkey auth in .NET 10 [src3, src8]
System.Drawing (GDI+)SkiaSharp or ImageSharpSystem.Drawing.Common is Windows-only in .NET 6+ [src1]
AppDomain.CreateDomain()Separate process or AssemblyLoadContextAppDomains not supported; use containers for isolation [src1]
.NET RemotinggRPC, REST, System.IO.Pipes, or StreamJsonRpcRemoting calls throw PlatformNotSupportedException [src1]
BundleConfig.cs (bundling)Webpack, Vite, or dotnet bundleBuilt-in bundling removed; use standard frontend tooling [src3]
BinaryFormatterSystem.Text.Json or MessagePackBinaryFormatter fully removed in .NET 10 [src9]

Decision Tree

START
|-- Is the app ASP.NET Web Forms?
|   |-- YES -> No direct port path. Rewrite in Blazor Server/WASM or Razor Pages [src1]
|   +-- NO v
|-- Is the app a class library or console app?
|   |-- YES -> Convert to SDK-style project, retarget to net10.0, fix API incompatibilities [src1]
|   +-- NO v
|-- Is the app ASP.NET MVC / Web API?
|   |-- YES -> Is it a large production app (>50K LOC)?
|   |   |-- YES -> Use incremental migration (YARP proxy + Strangler Fig) [src5]
|   |   +-- NO -> Use GitHub Copilot app modernization or legacy Upgrade Assistant [src4]
|   +-- NO v
|-- Is it WinForms or WPF?
|   |-- YES -> Convert to SDK-style project, retarget to net10.0-windows [src1]
|   +-- NO v
|-- Does it use WCF server-side?
|   |-- YES -> Port to CoreWCF 2.x (API-compatible) or rewrite as gRPC/REST [src7]
|   +-- NO v
+-- DEFAULT -> Analyze with GitHub Copilot app modernization or legacy Upgrade Assistant [src4]

Step-by-Step Guide

1. Assess dependencies and compatibility

Run the GitHub Copilot app modernization agent (VS 2026) or the legacy .NET Upgrade Assistant analysis to identify incompatible APIs, unsupported technologies, and third-party library gaps. [src1, src4]

# Option A: Legacy CLI (deprecated but functional)
dotnet tool install -g upgrade-assistant
upgrade-assistant analyze <MySolution.sln>

# Option B: In Visual Studio 2026, open Copilot Chat and type:
# "Analyze this solution for migration from .NET Framework to .NET 10"

Verify: Review the generated report. Note any APIs flagged as unsupported.

2. Retarget .NET Framework to at least 4.7.2

Ensure your project targets .NET Framework 4.7.2 or later. This maximizes API overlap with .NET Standard 2.0. [src1]

<!-- Update TargetFrameworkVersion -->
<PropertyGroup>
  <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
</PropertyGroup>

Verify: msbuild /t:Restore && msbuild completes with no errors. Run full test suite.

3. Convert to SDK-style project format

Migrate from legacy .csproj format to SDK-style. Required for targeting modern .NET. [src1]

# Use try-convert tool
dotnet tool install -g try-convert
try-convert --project <MyProject.csproj>

Verify: dotnet build succeeds. NuGet packages use PackageReference.

4. Migrate NuGet packages to PackageReference

Convert from packages.config to PackageReference format. [src1]

<!-- In .csproj: replace packages.config with -->
<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

Verify: dotnet restore succeeds. Delete the old packages.config.

5. Retarget to .NET 10

Change target framework to net10.0. Fix compilation errors from missing APIs. [src1, src8]

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
# Add Windows Compatibility Pack for missing APIs
dotnet add package Microsoft.Windows.Compatibility

Verify: dotnet build succeeds. Run tests. Fix any PlatformNotSupportedException.

6. Migrate ASP.NET-specific code

For ASP.NET MVC/WebAPI, replace System.Web with ASP.NET Core equivalents. For large apps, use incremental YARP proxy approach. [src3, src5]

// BEFORE: using System.Web.Mvc;
// public class HomeController : Controller { public ActionResult Index() ... }

// AFTER: using Microsoft.AspNetCore.Mvc;
// public class HomeController : Controller { public IActionResult Index() ... }

Verify: Routes return the same responses. Compare HTTP status codes and response bodies.

7. Replace unsupported technologies

Migrate WCF, Entity Framework 6, and other unsupported libraries to modern equivalents. [src1, src7]

// WCF -> gRPC (or CoreWCF for direct port)
// EF6 DbContext -> EF Core 10 DbContext with OnConfiguring

Verify: All database queries return correct results. Run integration tests.

Code Examples

C#: Migrating HTTP Module to ASP.NET Core Middleware

// Input:  An ASP.NET Framework HTTP module (IHttpModule) for request logging
// Output: Equivalent ASP.NET Core middleware with DI support

// BEFORE: ASP.NET Framework HTTP Module
// public class RequestLoggingModule : IHttpModule
// {
//     public void Init(HttpApplication application)
//     {
//         application.BeginRequest += (s, e) => {
//             var context = ((HttpApplication)s).Context;
//             Debug.WriteLine($"Request: {context.Request.Url}");
//         };
//     }
//     public void Dispose() { }
// }

// AFTER: ASP.NET Core Middleware
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next,
        ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation("Request: {Method} {Path}",
            context.Request.Method, context.Request.Path);
        await _next(context);
        _logger.LogInformation("Response: {StatusCode}",
            context.Response.StatusCode);
    }
}

// Registration in Program.cs:
// app.UseMiddleware<RequestLoggingMiddleware>();

C#: Migrating Web.config to appsettings.json with Options pattern

// Input:  XML Web.config AppSettings + ConfigurationManager usage
// Output: JSON appsettings.json + IOptions<T> injection

// BEFORE: Web.config
// <appSettings>
//   <add key="SmtpHost" value="smtp.example.com" />
//   <add key="SmtpPort" value="587" />
// </appSettings>
// Usage: ConfigurationManager.AppSettings["SmtpHost"]

// AFTER: appsettings.json
// { "EmailSettings": { "SmtpHost": "smtp.example.com", "SmtpPort": 587 } }

public class EmailSettings
{
    public string SmtpHost { get; set; } = "";
    public int SmtpPort { get; set; } = 587;
    public int MaxRetries { get; set; } = 3;
}

// Registration: builder.Services.Configure<EmailSettings>(
//     builder.Configuration.GetSection("EmailSettings"));

public class EmailService
{
    private readonly EmailSettings _settings;

    public EmailService(IOptions<EmailSettings> options)
    {
        _settings = options.Value;
    }

    public void Send(string to, string subject, string body)
    {
        using var client = new SmtpClient(_settings.SmtpHost, _settings.SmtpPort);
        // send logic with _settings.MaxRetries retry attempts
    }
}

C#: Migrating Global.asax to Program.cs (.NET 10 minimal hosting)

// Input:  ASP.NET Framework Global.asax with Application_Start
// Output: ASP.NET Core Program.cs with equivalent startup and middleware

// BEFORE: Global.asax.cs
// public class MvcApplication : HttpApplication {
//     protected void Application_Start() {
//         AreaRegistration.RegisterAllAreas();
//         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//         RouteConfig.RegisterRoutes(RouteTable.Routes);
//     }
// }

// AFTER: Program.cs (.NET 10)
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});
builder.Services.AddLogging();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Anti-Patterns

Wrong: Using ConfigurationManager in ASP.NET Core

// BAD -- ConfigurationManager does not work with appsettings.json
using System.Configuration;
var host = ConfigurationManager.AppSettings["SmtpHost"]; // Returns null

Correct: Use IConfiguration or IOptions

// GOOD -- Built-in configuration reads appsettings.json
public class EmailService
{
    public EmailService(IConfiguration config)
    {
        var host = config["EmailSettings:SmtpHost"];
    }
}

Wrong: Referencing System.Web types in shared libraries

// BAD -- System.Web does not exist in .NET Core
using System.Web;
public string GetClientIp(HttpContext context)
{
    return context.Request.UserHostAddress;
}

Correct: Use Microsoft.AspNetCore.Http

// GOOD -- ASP.NET Core HttpContext
using Microsoft.AspNetCore.Http;
public string GetClientIp(HttpContext context)
{
    return context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
}

Wrong: Copying entire .csproj and manually editing

<!-- BAD -- Old-format project files have hundreds of lines -->
<Project ToolsVersion="15.0" DefaultTargets="Build"
  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\..." />
  <!-- 200+ lines -->
</Project>

Correct: Use SDK-style project format

<!-- GOOD -- SDK-style: clean, minimal -->
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
  </ItemGroup>
</Project>

Wrong: Big-bang rewrite of the entire application

// BAD -- Rewriting everything at once stalls feature development
// "Freeze features and rewrite the entire 200K LOC app in .NET 10"
// Result: 6+ months delay, regressions, team burnout

Correct: Incremental migration using YARP proxy

// GOOD -- Strangler Fig: migrate one route at a time
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// Proxy unmigrated routes to legacy .NET Framework app

Wrong: Using BinaryFormatter for serialization

// BAD -- BinaryFormatter fully removed in .NET 10 (disabled .NET 8, removed .NET 9+)
var formatter = new BinaryFormatter();
formatter.Serialize(stream, myObject); // Compile error in .NET 10

Correct: Use System.Text.Json or protobuf

// GOOD -- System.Text.Json is the default serializer
using System.Text.Json;
var json = JsonSerializer.Serialize(myObject);
var obj = JsonSerializer.Deserialize<MyType>(json);

Wrong: Targeting .NET 8 for new migrations in 2026

<!-- BAD -- .NET 8 LTS support ends November 2026 -->
<TargetFramework>net8.0</TargetFramework>

Correct: Target .NET 10 LTS directly

<!-- GOOD -- .NET 10 LTS supported until November 2028 -->
<TargetFramework>net10.0</TargetFramework>

Common Pitfalls

Diagnostic Commands

# Check installed .NET SDKs and runtimes
dotnet --list-sdks
dotnet --list-runtimes

# Analyze a project for migration issues (legacy CLI, deprecated)
dotnet tool install -g upgrade-assistant
upgrade-assistant analyze <MySolution.sln>

# Check API compatibility against .NET 10
dotnet tool install -g apicompat
apicompat -a <MyAssembly.dll> -f net10.0

# List NuGet packages and check for updates
dotnet list package --outdated
dotnet list package --vulnerable

# Convert old project format to SDK-style
dotnet tool install -g try-convert
try-convert --project <MyProject.csproj>

# Scaffold EF Core context from existing database
dotnet ef dbcontext scaffold "Server=.;Database=MyDb;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models

# Verify target framework and SDK version
dotnet --info

Version History & Compatibility

VersionStatusReleaseKey Migration Notes
.NET 10LTS (Current)Nov 2025Recommended target. LTS until Nov 2028. C# 14, AI integration, NativeAOT, post-quantum crypto, passkey auth.
.NET 9EOL (May 2026)Nov 2024STS -- 18 months. AOT improvements. Migrate to .NET 10 before May 2026.
.NET 8LTS (EOL Nov 2026)Nov 2023LTS until Nov 2026. Blazor SSR, Native AOT for ASP.NET Core. Plan upgrade to .NET 10.
.NET 7EOL (May 2024)Nov 2022Skip -- go directly to .NET 10.
.NET 6EOL (Nov 2024)Nov 2021Minimal hosting introduced. Was LTS.
.NET 5EOL (May 2022)Nov 2020First unified .NET brand. Skip.
.NET Core 3.1EOL (Dec 2022)Dec 2019Last "Core" version. WinForms/WPF added.
.NET Framework 4.8.1MaintainedAug 2022Last .NET Framework release. Security patches only.

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Starting any new .NET projectApp exclusively uses Web Forms with no rewrite budgetStay on .NET Framework 4.8
App needs cross-platform (Linux containers)Small internal tool working fine on .NET FrameworkMaintain as-is until EOL pressure
Performance is critical (Kestrel is faster than IIS+System.Web)Heavy COM+ / EnterpriseServices dependencyStay on .NET Framework or refactor first
Team wants modern C# features (records, primary constructors)Massive WCF server with hundreds of contractsPort to CoreWCF first
Hosting costs matter (Linux is cheaper)All third-party deps target only .NET FrameworkWait for package updates
Need Native AOT or container deploymentProject in maintenance mode, no active developmentLeave on .NET Framework
Need AI integration (Microsoft.Extensions.AI, MCP support)Migration budget exhausted and .NET Framework still patchedDefer migration

Important Caveats

Related Units