---
# === IDENTITY ===
id: software/migrations/dotnet-framework-to-core/2026
canonical_question: "How do I migrate from .NET Framework to .NET Core/.NET 10?"
aliases:
  - ".NET Framework to .NET Core migration guide"
  - "migrate .NET Framework to .NET 10"
  - "port .NET Framework to modern .NET"
  - "upgrade .NET Framework 4.8 to .NET 10"
  - ".NET Framework modernization guide"
  - "convert .NET Framework project to .NET Core"
  - "ASP.NET to ASP.NET Core migration"
  - "migrate .NET Framework to .NET 8"
entity_type: software_reference
domain: software > migrations > dotnet_framework_to_core
region: global
jurisdiction: global
temporal_scope: 2016-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.94
version: 2.1
first_published: 2026-02-17

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: "GitHub Copilot app modernization cross-platform GA (2026-03-12)"
  next_review: 2026-11-13
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Target .NET 10 (LTS, supported until November 10, 2028) for new migrations -- both .NET 8 LTS and .NET 9 STS end on November 10, 2026"
  - "Web Forms, Windows Workflow Foundation (WF), and AppDomains have NO equivalent in modern .NET -- these require a full rewrite"
  - "BinaryFormatter is completely removed in .NET 10 -- switch to System.Text.Json or MessagePack before migrating"
  - "The .NET Upgrade Assistant CLI is deprecated as of 2025 -- the replacement is the GitHub Copilot app modernization agent (cross-platform across VS, VS Code, GitHub Copilot CLI, and GitHub.com since 2026-03-12; requires a paid Copilot subscription)"
  - "System.Drawing.Common throws PlatformNotSupportedException on Linux/macOS in .NET 6+ -- use SkiaSharp or ImageSharp for cross-platform"
  - "Code Access Security (CAS) and Security Transparency are not supported in .NET Core/10 -- use OS-level security boundaries instead"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User wants to migrate between modern .NET versions (e.g., .NET 8 to .NET 10)"
    use_instead: "Consult Microsoft's version-specific migration guide at learn.microsoft.com/aspnet/core/migration/90-to-100"
  - condition: "User is building a new project from scratch (not migrating)"
    use_instead: "Start directly with .NET 10 -- no migration needed"
  - condition: "User wants to migrate ASP.NET Core between versions, not from .NET Framework"
    use_instead: "Microsoft's ASP.NET Core version migration docs at learn.microsoft.com"

# === AGENT HINTS ===
inputs_needed:
  - key: app_type
    question: "What type of .NET Framework application are you migrating?"
    type: choice
    options: ["ASP.NET MVC/WebAPI", "ASP.NET Web Forms", "WinForms/WPF", "Console app/class library", "WCF service"]
  - key: codebase_size
    question: "How large is the codebase?"
    type: choice
    options: ["Small (<10K LOC)", "Medium (10K-100K LOC)", "Large (>100K LOC)"]
  - key: target_platform
    question: "What is the target deployment platform?"
    type: choice
    options: ["Windows only", "Cross-platform (Linux containers)", "Azure App Service"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/migrations/dotnet-framework-to-core/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-05-17)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/migrations/jquery-to-react/2026"
      label: "jQuery to React Migration"
    - id: "software/migrations/javascript-to-typescript/2026"
      label: "JavaScript to TypeScript Migration"
  alternative_to:
    - id: "software/migrations/dotnet-framework-to-core/2026"
      label: "Stay on .NET Framework 4.8.1 (security patches only)"
  often_confused_with:
    - id: "software/migrations/dotnet-8-to-10/2026"
      label: "Migrating between modern .NET versions (not from Framework)"

# === SOURCES (9 authoritative sources) ===
# Types: official_docs, technical_blog, rfc_spec, academic_paper, community_resource, industry_report
# Reliability: high, moderate_high, moderate, moderate_low, low, authoritative
sources:
  - id: src1
    title: "Port from .NET Framework to .NET"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dotnet/core/porting/framework-overview
    type: official_docs
    published: 2025-09-15
    reliability: authoritative
  - id: src2
    title: "Breaking changes - .NET Framework to .NET Core"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core
    type: official_docs
    published: 2024-05-01
    reliability: authoritative
  - id: src3
    title: "Migrate from ASP.NET Framework to ASP.NET Core"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/aspnet/core/migration/fx-to-core/?view=aspnetcore-10.0
    type: official_docs
    published: 2025-12-04
    reliability: authoritative
  - id: src4
    title: ".NET Upgrade Assistant Overview"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview
    type: official_docs
    published: 2024-10-08
    reliability: authoritative
  - id: src5
    title: "Get started with incremental ASP.NET to ASP.NET Core migration"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/aspnet/core/migration/fx-to-core/start?view=aspnetcore-9.0
    type: official_docs
    published: 2025-12-04
    reliability: authoritative
  - id: src6
    title: "Migrate HTTP modules to ASP.NET Core middleware"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/aspnet/core/migration/http-modules?view=aspnetcore-8.0
    type: official_docs
    published: 2025-07-17
    reliability: authoritative
  - id: src7
    title: ".NET Migration Guide: Framework 4.8 to .NET 10"
    author: Wojciechowski
    url: https://wojciechowski.app/en/articles/dotnet-migration-guide
    type: technical_blog
    published: 2025-11-01
    reliability: high
  - id: src8
    title: "Announcing .NET 10"
    author: Microsoft
    url: https://devblogs.microsoft.com/dotnet/announcing-dotnet-10/
    type: official_docs
    published: 2025-11-11
    reliability: authoritative
  - id: src9
    title: "Breaking changes in .NET 10"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dotnet/core/compatibility/10
    type: official_docs
    published: 2025-11-11
    reliability: authoritative
  - id: src10
    title: "Modernize .NET Anywhere with GitHub Copilot"
    author: Microsoft
    url: https://devblogs.microsoft.com/dotnet/modernize-dotnet-anywhere-with-ghcp/
    type: official_docs
    published: 2026-03-12
    reliability: authoritative
  - id: src11
    title: ".NET and .NET Framework May 2026 servicing releases updates"
    author: Microsoft
    url: https://devblogs.microsoft.com/dotnet/dotnet-and-dotnet-framework-may-2026-servicing-updates/
    type: official_docs
    published: 2026-05-12
    reliability: authoritative
---

# How to Migrate from .NET Framework to .NET 10 (formerly .NET Core)

## TL;DR

- **Bottom line**: Migrate incrementally using the Strangler Fig pattern -- set up an ASP.NET Core app as a YARP proxy in front of your .NET Framework app, then move routes one at a time. Target .NET 10 (LTS, supported until November 10, 2028) and use the GitHub Copilot app modernization agent for AI-assisted code fixes. As of March 2026 the agent runs across Visual Studio, VS Code, GitHub Copilot CLI, and GitHub.com -- the previous Visual-Studio-only restriction is gone. [src10]
- **Key tool/command**: GitHub Copilot Chat with the modernize-dotnet agent (`@modernize-dotnet upgrade this solution to .NET 10`). For the deprecated legacy CLI: `dotnet tool install -g upgrade-assistant && upgrade-assistant upgrade <project.csproj>` (still functional in VS 2026 via Tools > Options > Projects and Solutions > Modernization > "Enable legacy Upgrade Assistant").
- **Watch out for**: Technologies with no .NET Core equivalent -- WCF server-side, Web Forms, Windows Workflow Foundation, and AppDomains are not available in modern .NET. BinaryFormatter is fully removed in .NET 10. [src1]
- **Works with**: .NET Framework 4.5+ to .NET 8/9/10, Windows Forms, WPF, ASP.NET MVC/WebAPI, console apps, class libraries.
- **Latest servicing**: .NET 10.0.8 (May 12, 2026) -- pin to the newest patch when starting a migration. [src11]

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Target .NET 10 (LTS, November 10, 2028 EOL) for all new migrations. After Microsoft extended STS support from 18 to 24 months, both .NET 8 LTS and .NET 9 STS now end on the same day -- November 10, 2026 -- so neither is a long-term destination. [src8]
- Web Forms, Windows Workflow Foundation (WF), and AppDomains have NO direct equivalent in modern .NET. These require a full rewrite (Blazor/Razor Pages for Web Forms, Elsa/CoreWF for WF, AssemblyLoadContext for AppDomains). [src1]
- BinaryFormatter is completely removed in .NET 10 (was disabled-by-default in .NET 8, removed entirely in .NET 9+). All serialization must use System.Text.Json, MessagePack, or Protocol Buffers. [src9]
- The .NET Upgrade Assistant CLI is officially deprecated as of 2025. The replacement is the GitHub Copilot app modernization agent, which since March 12, 2026 runs across Visual Studio, VS Code, GitHub Copilot CLI, and GitHub.com (cross-platform; .NET Framework migrations still require Windows). Requires a paid Copilot SKU (Pro, Pro+, Business, or Enterprise). The legacy CLI is still installable but receives no updates. [src4, src10]
- System.Drawing.Common throws `PlatformNotSupportedException` on Linux/macOS in .NET 6+. Cross-platform apps must use SkiaSharp or ImageSharp. [src1]
- Code Access Security (CAS) and Security Transparency are not supported. Use OS-level security (containers, user accounts) instead. [src1]

## Quick Reference

| .NET Framework Pattern | .NET 10 Equivalent | Notes |
|---|---|---|
| `System.Web.HttpContext` | `Microsoft.AspNetCore.Http.HttpContext` | Completely different API -- use System.Web adapters for incremental migration [src5] |
| `Web.config` (XML) | `appsettings.json` + Options pattern | Use `IOptions<T>` / `IConfiguration` for strongly-typed settings [src6] |
| `Global.asax` / `Application_Start` | `Program.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` endpoints | Use minimal APIs or controller endpoints [src6] |
| `packages.config` | `PackageReference` in `.csproj` | Migrate 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 or CoreWCF or REST API | CoreWCF 2.x supports .NET 8/10; gRPC recommended for new services [src7] |
| Entity Framework 6 | Entity Framework Core 10 | Different API surface; migrations not directly compatible. Use `dotnet ef dbcontext scaffold` [src2] |
| `System.Web.Mvc.Controller` | `Microsoft.AspNetCore.Mvc.Controller` | Namespace swap; action filters and model binding differ [src3] |
| `FormsAuthentication` | ASP.NET Core Identity / Cookie Auth | Use `AddAuthentication().AddCookie()`; passkey auth available in .NET 10 [src3, src8] |
| `System.Drawing` (GDI+) | SkiaSharp or ImageSharp | `System.Drawing.Common` is Windows-only in .NET 6+ [src1] |
| `AppDomain.CreateDomain()` | Separate process or `AssemblyLoadContext` | AppDomains not supported; use containers for isolation [src1] |
| `.NET Remoting` | gRPC, REST, `System.IO.Pipes`, or StreamJsonRpc | Remoting calls throw `PlatformNotSupportedException` [src1] |
| `BundleConfig.cs` (bundling) | Webpack, Vite, or `dotnet bundle` | Built-in bundling removed; use standard frontend tooling [src3] |
| `BinaryFormatter` | `System.Text.Json` or MessagePack | BinaryFormatter 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 ASP.NET Core 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 agent 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]
```

## Decision Logic

### If targeting a NEW migration in 2026 with at least 18 months of maintenance ahead
--> Target .NET 10 directly. Skip .NET 8 and .NET 9 -- both end support on November 10, 2026. [src8, src11]

### If the app is ASP.NET MVC or Web API and larger than ~50K lines of code
--> Use the incremental approach: an ASP.NET Core host with YARP reverse proxy and `Microsoft.AspNetCore.SystemWebAdapters` for shared session/auth, migrating one route at a time. [src5]

### If the app is ASP.NET Web Forms
--> Do NOT attempt a port. Rewrite as Blazor Server, Blazor United, or ASP.NET Core Razor Pages. There is no automated migration path. [src1]

### If the app depends on WCF server-side
--> Port to CoreWCF 2.x for binary client compatibility; rewrite as gRPC or REST only if you control the clients. [src7]

### If the app uses BinaryFormatter for caching or serialization
--> Replace it BEFORE retargeting to .NET 10 -- BinaryFormatter is compile-time removed in .NET 10. Use System.Text.Json for safe payloads or MessagePack/protobuf for binary throughput. [src9]

### If the team has a paid GitHub Copilot subscription
--> Use the `modernize-dotnet` Copilot agent (VS, VS Code, Copilot CLI, or GitHub.com since March 2026). Always re-run `dotnet restore` to flag hallucinated NuGet package names before committing. [src4, src10]

### If the team has NO Copilot subscription and wants a free tool
--> Use the legacy .NET Upgrade Assistant (`dotnet tool install -g upgrade-assistant`). It receives no updates but is still functional; in VS 2026 enable it under Tools > Options > Projects and Solutions > Modernization. [src4]

### If the app must run on Linux containers
--> Audit for `System.Drawing.Common` (replace with SkiaSharp or ImageSharp) and any Windows-only Compatibility Pack APIs before retargeting. Test on the target Linux distro -- many APIs throw `PlatformNotSupportedException` at runtime, not at build. [src1]

## 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]

```bash
# 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 (AppDomains, Remoting, CAS, WCF, Web Forms). Count third-party NuGet packages that lack .NET 10 support.

### 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 and reduces porting friction. [src1]

```xml
<!-- Before: old-style project -->
<PropertyGroup>
  <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>

<!-- After: still .NET Framework, but latest -->
<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 the legacy `.csproj` format to SDK-style. This is required for targeting modern .NET. [src1]

```bash
# Option A: Use try-convert tool
dotnet tool install -g try-convert
try-convert --project <MyProject.csproj>

# Option B: Use legacy Upgrade Assistant
upgrade-assistant upgrade <MyProject.csproj>

# Option C: In Visual Studio 2026, use Copilot app modernization agent
```

After conversion, your `.csproj` should look like:

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net472</TargetFramework>
    <OutputType>Library</OutputType>
  </PropertyGroup>
</Project>
```

**Verify**: `dotnet build` succeeds. Verify NuGet packages use `PackageReference` instead of `packages.config`.

### 4. Migrate NuGet packages to PackageReference

Convert from `packages.config` to `PackageReference` format. [src1]

```bash
# In Visual Studio: right-click packages.config -> "Migrate packages.config to PackageReference"
# Or manually: delete packages.config and add <PackageReference> items to .csproj
```

```xml
<!-- Before: packages.config -->
<packages>
  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
</packages>

<!-- After: in .csproj -->
<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
```

**Verify**: `dotnet restore` succeeds. Delete the old `packages.config` file.

### 5. Retarget to .NET 10

Change the target framework from `net472` to `net10.0` (or `net10.0-windows` for WinForms/WPF). Fix compilation errors from missing APIs. [src1, src8]

```xml
<PropertyGroup>
  <!-- For libraries, console apps, ASP.NET Core -->
  <TargetFramework>net10.0</TargetFramework>

  <!-- For WinForms/WPF (Windows-only) -->
  <TargetFramework>net10.0-windows</TargetFramework>
</PropertyGroup>
```

```bash
# Add Windows Compatibility Pack if needed for missing .NET Framework APIs
dotnet add package Microsoft.Windows.Compatibility
```

**Verify**: `dotnet build` succeeds. Run tests. Fix any `PlatformNotSupportedException` at runtime.

### 6. Migrate ASP.NET-specific code (if applicable)

For ASP.NET MVC/WebAPI apps, replace `System.Web` with ASP.NET Core equivalents. This is the most labor-intensive step. [src3, src5]

```csharp
// BEFORE: ASP.NET Framework controller
using System.Web.Mvc;

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

// AFTER: ASP.NET Core controller
using Microsoft.AspNetCore.Mvc;

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}
```

For large apps, use the incremental YARP proxy approach:

```csharp
// Program.cs -- ASP.NET Core app proxying to .NET Framework app
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddSystemWebAdapters()
    .AddRemoteAppClient(options =>
    {
        options.RemoteAppUrl = new Uri("https://localhost:44300");
        options.ApiKey = "my-secret-key";
    });

var app = builder.Build();
app.UseSystemWebAdapters();
app.MapReverseProxy();
app.Run();
```

**Verify**: Routes return the same responses as the original .NET Framework app. Compare HTTP status codes and response bodies.

### 7. Replace unsupported technologies

Migrate WCF, Entity Framework 6, and other unsupported libraries to their modern equivalents. [src1, src7]

```csharp
// WCF service -> gRPC service
// BEFORE: WCF
[ServiceContract]
public interface IGreeterService
{
    [OperationContract]
    string SayHello(string name);
}

// AFTER: gRPC (.proto file)
// syntax = "proto3";
// service Greeter {
//   rpc SayHello (HelloRequest) returns (HelloReply);
// }
// message HelloRequest { string name = 1; }
// message HelloReply { string message = 1; }
```

```csharp
// EF6 -> EF Core 10
// BEFORE: Entity Framework 6
public class MyDbContext : System.Data.Entity.DbContext
{
    public DbSet<Product> Products { get; set; }
}

// AFTER: Entity Framework Core 10
public class MyDbContext : Microsoft.EntityFrameworkCore.DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlServer(connectionString);
}
```

**Verify**: All database queries return correct results. Run integration tests against staging database.

## Code Examples

### C#: Migrating HTTP Module to ASP.NET Core Middleware

> Full script: [c-migrating-http-module-to-asp-net-core-middleware.txt](scripts/c-migrating-http-module-to-asp-net-core-middleware.txt) (36 lines)

```csharp
// 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
// {
# ... (see full script)
```

### C#: Migrating Web.config to appsettings.json with Options pattern

> Full script: [c-migrating-web-config-to-appsettings-json-with-op.txt](scripts/c-migrating-web-config-to-appsettings-json-with-op.txt) (41 lines)

```csharp
// 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" />
# ... (see full script)
```

### C#: Migrating Global.asax to Program.cs

> Full script: [c-migrating-global-asax-to-program-cs.txt](scripts/c-migrating-global-asax-to-program-cs.txt) (43 lines)

```csharp
// Input:  ASP.NET Framework Global.asax with Application_Start, error handling
// Output: ASP.NET Core Program.cs with equivalent startup and middleware
// BEFORE: Global.asax.cs
// public class MvcApplication : System.Web.HttpApplication
// {
# ... (see full script)
```

## Anti-Patterns

### Wrong: Using ConfigurationManager in ASP.NET Core

```csharp
// BAD -- ConfigurationManager does not work with appsettings.json
using System.Configuration;
var host = ConfigurationManager.AppSettings["SmtpHost"]; // Returns null in .NET Core
```

### Correct: Use IConfiguration or IOptions

```csharp
// GOOD -- Built-in configuration system reads appsettings.json
public class EmailService
{
    public EmailService(IConfiguration config)
    {
        var host = config["EmailSettings:SmtpHost"]; // Reads from appsettings.json
    }
}
```

### Wrong: Referencing System.Web types in shared libraries

```csharp
// BAD -- System.Web does not exist in .NET Core
using System.Web;
public string GetClientIp(HttpContext context)
{
    return context.Request.UserHostAddress; // System.Web.HttpContext only
}
```

### Correct: Use Microsoft.AspNetCore.Http or System.Web adapters

```csharp
// 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

```xml
<!-- BAD -- Old-format project files have hundreds of lines of boilerplate -->
<Project ToolsVersion="15.0" DefaultTargets="Build"
  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\..." />
  <!-- 200+ lines of assembly references, compile items, etc. -->
</Project>
```

### Correct: Use SDK-style project format

```xml
<!-- GOOD -- SDK-style project: clean, minimal, auto-includes files -->
<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

```csharp
// BAD -- Rewriting everything at once stalls feature development for months
// "We'll freeze features and rewrite the entire 200K LOC app in .NET 10"
// Result: 6+ months of no features, regressions, team burnout
```

### Correct: Incremental migration using YARP proxy

```csharp
// GOOD -- Strangler Fig pattern: migrate one route at a time
// ASP.NET Core handles migrated routes, proxies everything else to .NET Framework
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

// appsettings.json:
// "ReverseProxy": {
//   "Routes": { "fallback": { "ClusterId": "legacy", "Match": { "Path": "{**catch-all}" } } },
//   "Clusters": { "legacy": { "Destinations": { "d1": { "Address": "https://localhost:44300" } } } }
// }
```

### Wrong: Using BinaryFormatter for serialization

```csharp
// BAD -- BinaryFormatter is fully removed in .NET 10 (disabled in .NET 8, removed in .NET 9+)
var formatter = new BinaryFormatter();
formatter.Serialize(stream, myObject); // Throws PlatformNotSupportedException in .NET 8, compile error in .NET 10
```

### Correct: Use System.Text.Json or protobuf

```csharp
// GOOD -- System.Text.Json is the default serializer in .NET Core/.NET 10
using System.Text.Json;
var json = JsonSerializer.Serialize(myObject);
var obj = JsonSerializer.Deserialize<MyType>(json);
```

### Wrong: Targeting .NET 8 for new migrations in 2026

```csharp
// BAD -- .NET 8 LTS support ends November 2026
// Starting a migration today that targets .NET 8 means you'll need to migrate again soon
<TargetFramework>net8.0</TargetFramework>
```

### Correct: Target .NET 10 LTS directly

```csharp
// GOOD -- .NET 10 LTS is supported until November 2028
<TargetFramework>net10.0</TargetFramework>
// If already on .NET 8, the upgrade to .NET 10 is straightforward (2-5 days for most projects)
```

## Common Pitfalls

- **Unsupported technologies with no migration path**: Web Forms, Windows Workflow Foundation (WF), and AppDomains have no direct equivalent in .NET Core. Fix: Rewrite Web Forms as Blazor or Razor Pages; replace WF with Elsa Workflows or CoreWF; use separate processes or `AssemblyLoadContext` instead of AppDomains. [src1]
- **Third-party NuGet packages targeting only .NET Framework**: Many older packages lack .NET Standard/Core targets. Fix: Check NuGet for updated versions, use `Microsoft.Windows.Compatibility` pack for missing Framework APIs, or find alternative packages. [src1]
- **EF6 migrations not compatible with EF Core**: Database migrations from Entity Framework 6 cannot be directly used with EF Core. Fix: Create a new initial EF Core migration from the existing database schema using `dotnet ef dbcontext scaffold`. [src2]
- **System.Drawing.Common is Windows-only in .NET 6+**: `System.Drawing.Common` throws `PlatformNotSupportedException` on Linux/macOS. Fix: Use SkiaSharp or ImageSharp for cross-platform image processing. [src1]
- **BinaryFormatter fully removed in .NET 10**: `BinaryFormatter` was disabled-by-default in .NET 8 and completely removed from the API in .NET 9/10. Fix: Switch to `System.Text.Json`, `MessagePack`, or Protocol Buffers for serialization. Audit all `[Serializable]` attributes. [src2, src9]
- **Session state API differences**: ASP.NET Framework and ASP.NET Core have fundamentally different session APIs. Fix: Use System.Web adapters for shared session during incremental migration, then migrate to `ISession` interface. [src5]
- **DI container mismatch**: ASP.NET Framework apps often use Unity, Autofac, or Ninject. ASP.NET Core has a built-in DI container. Fix: Register services in `Program.cs` using `builder.Services.AddScoped/Transient/Singleton<T>()`. Third-party containers like Autofac still work with ASP.NET Core. [src3]
- **`ProcessStartInfo.UseShellExecute` default changed**: Default is `false` in .NET Core (was `true` in .NET Framework). Fix: Explicitly set `UseShellExecute = true` if your code relies on shell execution. [src2]
- **GitHub Copilot app modernization agent introducing non-existent packages**: The AI-based tool occasionally suggests NuGet packages that don't exist. Fix: Always verify generated `PackageReference` entries with `dotnet restore` before continuing. Cross-check package names on nuget.org. [src4]

## Diagnostic Commands

```bash
# 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 .NET 10 support
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

# Check for .NET Framework API usage in compiled assemblies
dotnet tool install -g dotnet-apisof
dotnet-apisof <MyAssembly.dll>

# Verify target framework after migration
dotnet --info
```

## Version History & Compatibility

| Version | Status | Release | Key Migration Notes |
|---|---|---|---|
| .NET 10 | LTS (Current) | Nov 2025 | **Recommended target.** LTS until Nov 10, 2028. C# 14, AI integration, NativeAOT improvements, post-quantum cryptography, passkey auth. Latest servicing release: 10.0.8 (May 12, 2026). [src8, src11] |
| .NET 9 | STS (EOL Nov 10, 2026) | Nov 2024 | After Microsoft's STS extension from 18 to 24 months, .NET 9 support now lasts until Nov 10, 2026 -- the same day .NET 8 LTS expires. AOT improvements, new LINQ methods. Migrate to .NET 10 before Nov 2026. |
| .NET 8 | LTS (EOL Nov 10, 2026) | Nov 2023 | LTS until Nov 10, 2026. Blazor SSR, Native AOT for ASP.NET Core. After May 2026, only security fixes ship -- start planning the .NET 10 upgrade now. [src1] |
| .NET 7 | EOL (May 2024) | Nov 2022 | Skip -- go directly to .NET 10. |
| .NET 6 | EOL (Nov 2024) | Nov 2021 | Minimal hosting model introduced. Was LTS. |
| .NET 5 | EOL (May 2022) | Nov 2020 | First unified .NET brand. Skip. |
| .NET Core 3.1 | EOL (Dec 2022) | Dec 2019 | Last version branded "Core". WinForms/WPF added. |
| .NET Framework 4.8.1 | Maintained | Aug 2022 | Last .NET Framework release. Security patches only through Windows lifecycle. [src1] |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Starting any new .NET project | Your app exclusively uses Web Forms with no rewrite budget | Stay on .NET Framework 4.8 |
| App needs cross-platform (Linux containers) | Small internal tool working fine on .NET Framework | Maintain as-is until EOL pressure |
| Performance is critical (Kestrel is much faster than IIS+System.Web) | Heavy COM+ / EnterpriseServices dependency | Stay on .NET Framework or refactor COM dependencies first |
| Team wants modern C# features (records, pattern matching, primary constructors) | Massive WCF server with hundreds of contracts | Port to CoreWCF first, then consider full migration |
| Hosting costs matter (Linux hosting is cheaper) | All third-party dependencies target only .NET Framework | Wait for package updates or find alternatives |
| Need Native AOT or container-optimized deployment | Project is in maintenance mode with no active development | Leave on .NET Framework |
| Need AI integration features (Microsoft.Extensions.AI, MCP support) | Migration budget is exhausted and .NET Framework 4.8 still receives security patches | Defer migration |

## Important Caveats

- .NET Framework 4.8.1 is the last .NET Framework release. It will receive security patches through the Windows support lifecycle but no new features. Migration is inevitable for long-lived applications. [src1]
- The .NET Upgrade Assistant CLI is officially deprecated as of 2025. Microsoft recommends the GitHub Copilot app modernization agent (the `modernize-dotnet` Copilot agent) as the primary migration tool. As of March 12, 2026 the same workflow runs across Visual Studio, VS Code, GitHub Copilot CLI, and GitHub.com -- not just Visual Studio. The legacy CLI is still installable (and can be re-enabled in VS 2026 via Tools > Options > Projects and Solutions > Modernization) but receives no updates. Unlike the free Upgrade Assistant, Copilot modernization requires a paid Copilot SKU (Pro, Pro+, Business, or Enterprise). [src4, src10]
- `Microsoft.Windows.Compatibility` NuGet package provides many .NET Framework APIs on .NET Core, but some are Windows-only stubs that throw `PlatformNotSupportedException` on Linux/macOS. Always test on target platform. [src1]
- Code Access Security (CAS) and Security Transparency are not supported in .NET Core. Use OS-level security boundaries (containers, user accounts) instead. [src1]
- ASP.NET Core incremental migration using YARP and System.Web adapters requires both apps to run simultaneously during the migration period, which increases infrastructure complexity and cost. [src5]
- Multi-targeting (`<TargetFrameworks>net472;net10.0</TargetFrameworks>`) is useful for shared libraries during migration but doubles build and test time. [src7]
- .NET 10 requires Visual Studio 2022 version 17.12+ or Visual Studio 2026. Older VS versions cannot target .NET 10. [src8]
- .NET 10 includes post-quantum cryptography support (ML-KEM) and first-class MCP (Model Context Protocol) support for AI agent integration. [src8]

## Related Units

- [jQuery to React Migration](/software/migrations/jquery-to-react/2026)
- [JavaScript to TypeScript Migration](/software/migrations/javascript-to-typescript/2026)
