Pre-Migration Checklist for Moving from .NET Framework to .NET

· Updated: · · .NET, .NET Framework, C#, Modernization, Windows Development, Migration

Revision history (1 updates, last updated Sep 1, 2026)

A log of the changes made to this article. Where a pre-update version was archived, it stays readable at a permanent DOI link.

Retranslated as a full translation of the Japanese original. The previous English version was an abridgement that carried only part of the source, so sections, tables, Mermaid diagrams, figure captions and FAQ entries were missing. All of them have been restored to match the Japanese original, and the technical claims are the same as in the Japanese version. Read the version before this update (DOI: 10.5281/zenodo.21614503)
First published
Cite this article(DOI: 10.5281/zenodo.21614502)

This article is archived on Zenodo. Below are both the DOI that always resolves to the latest version and the DOI pinned to the version you are reading.

Go Komura (2026). Pre-Migration Checklist for Moving from .NET Framework to .NET. KomuraSoft LLC. https://doi.org/10.5281/zenodo.21614502 https://comcomponent.com/en/blog/2026/03/15/003-dotnet-framework-to-dotnet-premigration-checklist/

DOI (latest version)
10.5281/zenodo.21614502
DOI (this version)
10.5281/zenodo.22217147

Download the Excel checklist with Japanese and English sheets

The file has two sheets, Checklist-ja and Checklist-en, and turns the pre-start checklist of chapter 13 into 34 items across 7 categories: policy / preparing the current .NET Framework side / app type and technology choices / unsupported technologies and APIs to watch / Windows-only dependencies / shared libraries and data access / operations and build. The Status and Notes columns are left empty, so you can use it as a per-project inventory sheet as is. Skimming it before reading the article should make the flow up through chapter 13 easier to follow.

Change TargetFramework in the .csproj to net10.0, update a few NuGet packages, and once the build passes, you are done.

…If that were the migration, life would be pretty peaceful. In reality, it usually is not.

.NET Framework codebases are full of dormant assumptions you normally never think about: System.Web, WCF, Web Forms, old packages.config files, web.config.install.xdt, native DLLs, COM / ActiveX, third-party controls that only work at design time, implicit x86 assumptions, designer-dependent ResX, old serializers, and more.

So what really matters in a .NET Framework to .NET migration is the inventory work before you start implementing. If you can break the issues down before starting, the migration stops being a big gamble and becomes a list of items to knock down in order.

Taking stock changes the character of the migrationA diagram showing that if you take stock before starting implementation and break the issues down, the migration becomes a job of knocking items down in order rather than a big gamble.Skip taking stockDormant implicit assumptionsBreak the issues down by taking stock before startingIt becomes a job of knocking items down in orderThe migration becomes a big gamble

Figure 1: Whether you can break the issues down before starting decides whether the migration is a gamble or ordinary work.

This article summarizes what you should verify before migrating an existing .NET Framework 4.x line-of-business application to current .NET. The main targets are around here:

  • Class libraries
  • Console applications
  • Windows services
  • WinForms / WPF
  • ASP.NET Framework (MVC / Web API / Web Forms)
  • Applications that use WCF
  • Applications that use EF6

This was written as of 2026-03-15. Support windows and official tooling recommendations change, so if you are reading this much later, check the official information as well.

1. The Conclusion First

First, the conclusions that are hard to get wrong.

  • Clean up the .NET Framework side before migrating. Microsoft’s official guide also recommends upgrading to .NET Framework 4.7.2 or later before porting, and completing PackageReference conversion, SDK-style conversion, and dependency updates first.
  • Difficulty is determined by app model, not by code volume. Class libraries and console apps are relatively light; ASP.NET Framework, Web Forms, WCF servers, and WF (Workflow Foundation) tend to be heavy.
  • WinForms / WPF can migrate to .NET but remain Windows-only. Misunderstand this and you step on the classic landmine of migrating only to find the app will not run in a Linux container.
  • ASP.NET Framework to ASP.NET Core is effectively an architecture migration. Small apps can sometimes go in one shot, but for large production systems it is safer to plan on an incremental migration.
  • WCF and EF6 can sometimes be decoupled from the runtime migration. WCF clients have supported packages for .NET, and EF6 can be migrated to EF Core separately after the move to modern .NET.
  • On the other hand, AppDomain creation, .NET Remoting, CAS (Code Access Security), COM+, Workflow Foundation, and BinaryFormatter dependencies are red lights. Find them early, or the effort explodes later.
  • packages.config / install.ps1 / XDT (XML Document Transform, the mechanism that transforms files such as web.config) / content assets / native DLLs / COM / ActiveX / x86 assumptions tend to fail at runtime or design time even when the build passes, so flush them out before starting.
  • As of 2026-03, .NET 10 is the LTS. For new migrations, it is natural to target the current LTS as the landing point.
  • A migration without tests, measurements, and a rollback plan is dangerous. Migration is less an implementation task than a job of making assumptions visible, one at a time.

In the diagram a solid line marks a relation that always holds and a dashed line marks a conditional one (the conditions are given per relation on the detail page). The full list of relations (30 in total, with evidence and certainty) and the definitions of the main concepts are collected on the knowledge map detail page (in Japanese). Data: JSON-LD / Turtle

2. First Decide Whether You Should Migrate Right Now at All

The first thing to decide is not “how to migrate” but whether this app really should be migrated right now.

Leave this vague and you tend to end up with a migration that is technically correct but too heavy for the business, or the opposite: postponing far too long when migration is clearly the right call.

The question to settle firstA diagram showing that before deciding how to migrate you should decide whether this app really should be migrated now, and that leaving it vague tends to produce either a migration too heavy for the business or too much postponement.Decide this firstProceed while it stays vagueShould this really be migrated nowHow to migrateToo heavy a migration or too much postponement

Figure 2: Unless you settle whether to migrate now before asking how to migrate, the decision swings to one extreme or the other.

2.1 Staying on .NET Framework Is a Perfectly Valid Decision

.NET Framework 4.8.1 continues to be supported as long as it runs on a supported Windows. In other words, it is not as simple as “if you do not move everything to modern .NET right now, you are immediately at risk.”

However, staying has clear constraints.

  • You cannot escape being Windows-only
  • You keep carrying ASP.NET Web Forms and old server stacks
  • It is harder to benefit from new .NET performance improvements, language features, and the ecosystem
  • You drift away from the modern assumptions of cloud, containers, and CI/CD

Conversely, if dependencies like the following are strong, it is rational to consolidate on .NET Framework 4.8.1 for stable operation for now, while planning the replacement on a separate track.

Staying put as a rational choiceA diagram showing that with strong legacy dependencies such as Web Forms, WCF server compatibility, or COM+, it is rational to consolidate on .NET Framework 4.8.1 for stable operation for now while planning the replacement on a separate track.Strong legacy dependencies existConsolidate on 4.8.1 and run it stably for nowPlan the replacement on a separate trackConstraints such as Windows-only remain

Figure 3: When the dependencies run deep, stable operation on 4.8.1 plus a replacement plan becomes the realistic pair.

  • A large stock of Web Forms screens
  • Strict WCF server compatibility must be preserved
  • Deep Workflow Foundation or COM+ dependencies
  • Third-party design-time components do not support modern .NET
  • The business does not allow major specification changes

2.2 What Changes with Each Option

Option What gets better What remains / what you give up Good fit
Stay on .NET Framework 4.8.1 Easy to keep existing assets intact and operate stably Windows-only, old app models, limits on modernization Strong legacy dependencies; the business priority is stable operations for now
Migrate to modern .NET but stay on Windows Runtime and toolchain modernized. Big gains in performance, developer experience, SDK style Windows API dependencies remain. Not cross-platform WinForms / WPF, Windows services, business apps using Windows APIs
Migrate to modern .NET with Linux / containers / cloud in future scope More freedom in deployment targets. Easier to modernize the operations model Windows-only APIs and app models must be peeled off first You want to shift the server side toward the cloud and refresh infrastructure too

What matters is deciding not whether you want to migrate but where you want to land after the migration, first.

What to decide is the landing pointA diagram showing that what matters is deciding where you want to land after the migration rather than whether you want to migrate.Not enough as a questionDecide thisDo you want to migrateWhat to decide firstWhere you want to land after the migrationThe options and the issues to examine fall into place

Figure 4: Change the question from whether you want to migrate to where you want to land, and the options fall into place.

3. Four Policies to Decide Up Front

3.1 The Target .NET Version

At the time of writing, under Microsoft’s support policy, .NET 10 is the LTS. Meanwhile, .NET 8 LTS and .NET 9 STS are both scheduled to reach end of support on 2026-11-10. An STS release lands on the same date as an LTS one because the STS support window was extended from 18 months to 24 months. Counting with the older assumption that STS means 18 months makes .NET 9 look like it ends on 2026-05-12, so when you use a date as the basis for a decision, confirm it against the official lifecycle information.

So if you are now starting a fresh migration off .NET Framework, barring exceptional circumstances, landing on the current LTS is the natural choice.

The practical reasoning here is simple.

  • For a small migration you want done quickly, land directly on the current LTS
  • For a core system intended for long-term operation, still treat the current LTS as the baseline
  • “We want the previous LTS because of an existing library” is a legitimate reason, but judge it by checking the actual maintenance end date
How to think about the target versionA diagram showing that both small migrations and core systems treat the current LTS as the baseline, and that if an existing library pushes you toward the previous LTS you decide by checking the maintenance end date.BaselineConstraints from existing librariesChoose the target .NET versionLand on the current LTSConsider the previous LTS tooCheck the maintenance end date and decide

Figure 5: The current LTS is the default landing point, and choosing an older LTS has to be justified by the end-of-support date.

3.2 Stay Windows-Only, or Aim for Cross-Platform Later?

This decision dramatically changes which issues you need to look at.

  • If you stay Windows-only, you can take the pragmatic route of modernizing the runtime first, using WPF / WinForms and the Windows Compatibility Pack.
  • If you are aiming for Linux / containerization later, you need to inventory Windows-bound APIs early: System.Drawing.Common, the registry, WMI, EventLog, Windows services, COM, Office Interop, and so on.

Start migrating without deciding this, and midway through, the conversation gets twisted: “Was Windows-only fine after all?” “No wait, we wanted to run in containers.”

The Windows-only versus cross-platform forkA diagram showing that staying Windows-only lets you take the pragmatic route of modernizing the runtime first with the compatibility pack, while aiming at Linux or containers later requires inventorying Windows-bound APIs early.Stay Windows-onlyContainers later as wellStart without decidingWhich are you aiming forModernize the runtime with the compatibility packInventory Windows-bound APIs earlyThe conversation gets twisted midway

Figure 6: Without settling this fork first, the issues to examine never firm up and the plan gets twisted midway.

3.3 One Shot or Incremental?

There are broadly three migration shapes.

  • Near in-place, all-at-once migration
  • Side-by-side migration running old and new in parallel
  • Incremental migration, moving over route by route or library by library

Especially for ASP.NET Framework apps, Microsoft’s guidance explicitly recommends incremental migration. If you cannot stop production, have many features, and have many surrounding dependencies, it is less strained to design for incremental migration from the start.

Three shapes of migrationA diagram showing the three migration shapes of a near in-place all-at-once migration, a side-by-side migration running old and new in parallel, and an incremental migration by route or library, and that incremental migration is the premise when production cannot be stopped.Choose the migration shapeAll-at-once migration (near in-place)Side-by-side, old and new in parallelIncremental migration by route / libraryThe premise when production cannot stop

Figure 7: There are three shapes, and a large production system you cannot stop starts from incremental migration.

3.4 What to Exclude from This Migration

Migrations tend to fail because too much gets piled on.

For example, doing all of these at once tends to get heavy:

  • .NET Framework to .NET
  • ASP.NET Framework to ASP.NET Core
  • EF6 to EF Core
  • Windows servers to Linux containers
  • Changing the authentication infrastructure
  • Changing the logging / monitoring infrastructure
  • Migrating the database

Of course, all of them may eventually be necessary. But whether they must be done at the same time is a separate question.

In reality, separating things like this works better:

  1. First modernize the runtime and project structure
  2. Then move the app model
  3. Finally update the ORM, authentication, cloud, and monitoring
An order of separation that avoids piling everything onA diagram showing the order of separation in which the runtime and project structure are modernized first, then the app model is moved, and finally the ORM, authentication, cloud, and monitoring are updated.Pile them on at onceModernize the runtime and structureMove the app modelUpdate ORM, authentication, cloud, monitoringThe migration gets heavy and fails easily

Figure 8: Instead of doing everything at once, separating runtime, app model, and the surrounding systems keeps the work from getting heavy.

4. The Foundation to Prepare Before Starting

Microsoft’s pre-porting guide is quite practical. In essence: before migrating, bring your current .NET Framework projects up to a modern entry point.

4.1 Upgrade to .NET Framework 4.7.2 or Later

The official guide recommends targeting .NET Framework 4.7.2 or later before porting. The reason is that even when .NET Standard does not carry an existing API as is, it becomes easier to move toward newer API alternatives.

In practice, it is clearer to use 4.8.1 as the baseline if possible.

  • Straightforward from a support standpoint
  • Easy to treat as the final stable point on the .NET Framework side
  • Makes the policy of “first clean up on the current Framework side” easy to establish

What changes if you do this first

  • Handling of .NET Standard 2.0 shared libraries stabilizes more easily
  • Noise originating from the old runtime is reduced up front
  • It becomes easier to tell whether a compatibility issue is caused by “the Framework being old” or by “the move to modern .NET”

4.2 Move to PackageReference

The pre-porting guide recommends converting references to the PackageReference format. Doing this first significantly improves visibility into dependency management.

What changes with PackageReference

  • Package references are consolidated into the csproj
  • Transitive dependencies become easier to see
  • The restore assumptions line up with the modern .NET side
  • Better compatibility with the CLI / CI

However, there are landmines here.

Typical landmines

The official NuGet documentation explicitly lists these constraints for migrating from packages.config to PackageReference:

  • Visual Studio’s built-in migration cannot be used for ASP.NET projects
  • Packages that depend on install.ps1 / uninstall.ps1 may not work as expected
  • Assets in the content folder may be ignored
  • XDT transforms such as web.config.install.xdt are not applied
  • Packages with old assembly layouts directly under lib may not resolve well

In other words, do not think of it as merely changing the package format. Classic ASP.NET in particular had a strong culture of rewriting web.config at NuGet package install time, so implicit assumptions surface easily during migration.

Implicit assumptions exposed by the move to PackageReferenceA diagram showing that migrating from packages.config to PackageReference can leave install.ps1, XDT transforms, and content folder assets unapplied, which exposes the implicit assumption that web.config was rewritten at install time.Convert to PackageReferenceinstall.ps1 may not runXDT transforms are not appliedcontent assets may be ignoredImplicit assumptions get exposed

Figure 9: Even when you mean to change only the format, everything that relied on install-time machinery surfaces at once.

What to use for the conversion

Manual work is not the only option. Each tool covers a different range, though.

Tool What it does Assumptions and caveats
The Visual Studio conversion feature In Solution Explorer, right-click References or packages.config and choose Migrate packages.config to PackageReference... to run the conversion Visual Studio 2017 15.7 or later. Not available for ASP.NET and C++ projects. If the menu item does not appear, open NuGet restore or the package manager once and then right-click again
.NET Upgrade Assistant Analyzes and upgrades projects in one flow The official documentation marks it as deprecated and points you to GitHub Copilot app modernization
GitHub Copilot app modernization Supports assessment, planning, code fixes, and validation. It is the center of the current official guidance As covered in 4.5, it assumes Visual Studio, Copilot, and C# code
Manual SDK-style conversion Create a new csproj and move over only the entries you need For projects with few files, this is sometimes the fastest route

The Visual Studio conversion feature backs up the project before it runs and produces a conversion report at the end, covering top-level dependencies, transitive dependencies, and detected compatibility issues. To roll back, the documentation describes copying csproj and packages.config back from the backup folder and running update-package -reinstall in the Package Manager Console.

In other words, you can try it in a form you can undo. For a pre-start estimate, converting a single project and reading the report is the fastest way in.

Trying the conversion in a form you can undoA diagram showing that the Visual Studio conversion feature creates a backup before converting and produces a conversion report, so you can convert one project first, read the report, and restore from the backup if needed.If there are problemsIf there are noneA backup is createdConvert one project onlyRead the conversion reportRestore the files and reinstall the packagesRoll it out to the other projects

Figure 10: The conversion comes with a backup and a report, so start with one project to gauge the work.

4.3 Move to SDK-Style Projects

The pre-porting guide also recommends converting to the SDK-style project format.

This pays off substantially.

What changes with SDK style

  • The csproj becomes dramatically more concise
  • Plays well with PackageReference
  • Multi-targeting becomes easy
  • Easier to center CI/CD on dotnet build / dotnet test / dotnet publish
  • The configuration approaches the modern .NET side, reducing the diff in the later phase

Put the other way around: jumping straight to modern .NET with old csproj files and old NuGet management produces too large a diff.

Reducing the diff with SDK-style conversionA diagram showing that jumping straight to modern .NET with old csproj files and old NuGet management produces too large a diff, so converting to SDK style first brings the configuration closer to the modern .NET side and reduces the later diff.Jump straight acrossConvert to SDK style firstOld csproj plus old NuGet managementThe diff is too largeThe configuration approaches the modern sideThe later diff shrinks

Figure 11: Inserting the SDK-style conversion makes the jump to modern .NET one step smaller.

4.4 Update Dependencies First

This too follows the official guide: move dependencies to the latest available versions, and where possible to .NET Standard-compatible versions.

Why do this first

  • You find out early whether “this package works on modern .NET”
  • Prevents old dependencies from becoming noise
  • Makes it easier to convert shared libraries to netstandard2.0
  • Lets the later migration work focus on “porting the code”
Why updating dependencies first pays offA diagram showing that updating dependencies toward their latest versions first tells you early whether they work on modern .NET, removes noise from old dependencies, and lets the later migration work focus on porting the code.Update dependencies to the latest firstModern .NET support is known earlyNoise from old dependencies is reducedThe later phase focuses on porting code

Figure 12: The more dependency updates you finish up front, the more the migration itself is just porting.

4.5 Check the Assumptions of the Official Tooling Too

As of 2026-03, the center of gravity of Microsoft’s guidance has shifted toward GitHub Copilot app modernization. Rather than assuming only the traditional migration assistant tools, it matches practice better to view it as an end-to-end support flow covering assessment, planning, code fixes, and validation.

However, the current documentation assumes Visual Studio 2026 or a supported Visual Studio 2022 line, GitHub Copilot, and C# code.

Why this check is needed

  • It changes what you can expect from the official tooling
  • You can align the team’s assumptions about IDEs / build agents / extensions
  • You can make the call to not over-rely on automation for VB.NET solutions

Codebases with VB.NET mixed in are not rare. So it is worth confirming, at the very start, “how far the latest official tooling will actually help.”

Where the official tooling stands and what it assumesA diagram showing that .NET Upgrade Assistant is deprecated and the guidance now points to GitHub Copilot app modernization, which assumes Visual Studio, Copilot, and C# code, so VB.NET solutions call for not over-relying on automation.Deprecated, points hereIf VB.NET is in the mix.NET Upgrade AssistantGitHub Copilot app modernizationAssumes Visual Studio plus Copilot plus C#Do not over-rely on automation

Figure 13: The center of gravity has moved to Copilot app modernization, and codebases outside its assumptions need manual work.

5. Estimating Difficulty per Project Type

Migration tends to be discussed as one lump, “.NET Framework to .NET,” but in reality each project type is a different game.

5.1 A Rough Sense of Difficulty

Type Difficulty Main issues
Class library Low to medium API compatibility, dependencies, target splitting
Console / batch / some Windows services Low to medium Distribution method, native dependencies, configuration
WinForms / WPF Medium Remains Windows-only; designer, third-party UI, BinaryFormatter-related issues
ASP.NET MVC / Web API Medium to high App model migration to ASP.NET Core, authentication, session, configuration, DI
ASP.NET Web Forms High Large gap in page model; assume UI layer replacement
WCF client Medium Package replacement, contracts, configuration
WCF server High CoreWCF, or redesign as gRPC / HTTP API
EF6 to EF Core done simultaneously High The ORM is a different beast; behavioral differences; migration history

5.2 For Class Libraries, the Key Is How You Cut the “Shared Boundary”

Class libraries are relatively easy to move. But only when the library is genuinely separated like a library.

Dependencies like the following raise the difficulty:

  • Touching System.Web
  • Reading HttpContext.Current directly
  • Exposing WPF / WinForms types in the public API
  • Leaning heavily on Windows APIs such as the registry, WMI, EventLog
  • Depending on AppDomain or Remoting

A useful framing: if you can extract pure business logic, it is light; if the library has swallowed the app model, it is heavy.

How to judge the weight of a class libraryA diagram showing that a class library from which pure business logic can be extracted is light, while one that has swallowed the app model through System.Web, UI types, Windows APIs, or AppDomain is heavy.Only the logic needs extractingIt has swallowed the app modelLook at the class libraryThe migration is lightThe migration is heavySystem.Web, UI types, Windows API dependencies, and so on

Figure 14: A library’s difficulty comes not from line count but from how much of the app model it carries.

5.3 WinForms / WPF Migrate Easily, but Stay Windows-Only

WinForms and WPF can be migrated to .NET. However, both remain Windows-only frameworks.

Getting expectations wrong here is dangerous.

  • What improves
    • You get the modern .NET runtime, language, and SDK style
    • CI/CD and package management can be brought up to date
    • Some performance and maintainability improvements come along
  • What does not change
    • It is still Windows-only
    • Compatibility issues with UI controls and design-time components remain
    • ActiveX / COM / native DLL problems do not go away

Also, WinForms / WPF can require a BinaryFormatter impact review. Especially when custom types are involved in the clipboard, drag & drop, ResX, or design-time serialization, the issue tends to surface when you raise the target to .NET 9 or later.

Expectations for a WinForms / WPF migrationA diagram showing that WinForms and WPF can migrate to .NET and pick up the modern .NET runtime, language, and SDK style, but stay Windows-only and may require a BinaryFormatter impact review.Migrate WinForms / WPFPick up the benefits of modern .NETStill Windows-onlyA BinaryFormatter review is needed

Figure 15: Desktop UI can migrate, but the Windows-only nature carries straight over.

5.4 ASP.NET Framework Is an “App Model Migration,” Not a “Runtime Migration”

Migrating from ASP.NET Framework to ASP.NET Core is explicitly described as non-trivial in Microsoft’s guidance. Not merely because API names change, but because the underlying architecture is different.

The differences tend to show up around here:

  • Hosting model
  • Middleware pipeline
  • Request processing model
  • Session / Cache
  • Authentication / Authorization
  • Configuration
  • Dependency Injection
  • Logging / Monitoring

For an ASP.NET Framework app, the things to check first are around here:

  • Which routes / endpoints can be moved first
  • Whether System.Web dependencies can be peeled out of shared libraries
  • How to align authentication / session / exception handling / logging
  • Whether to migrate incrementally without stopping production

For large apps in particular, it is more realistic to assume incremental migration from the start.

The ASP.NET migration is an app model migrationA diagram showing that moving from ASP.NET Framework to ASP.NET Core is not a replacement of API names but an app model migration in which hosting, middleware, authentication, configuration, and DI all change, so large apps realistically plan for incremental migration.Large appsASP.NET Framework to CoreThe underlying architecture changeshosting, middleware, auth, configuration, DIPlan on incremental migration

Figure 16: The ASP.NET migration moves the app model rather than the runtime, and the larger it is the more incremental it becomes.

5.5 For Web Forms, Start with “Decomposing Responsibilities,” Not “Migrating Assets”

Web Forms is not the same app model as ASP.NET Core. So for estimation purposes, it is safer not to assume the screen assets can be carried over as is.

In practice, the usual entry point is this decomposition:

  • Separate screen logic from business logic
  • Break apart the responsibilities buried in Page / UserControl / ViewState
  • Move business logic and data access out into shared libraries
  • Rebuild the UI on a different model: Razor Pages / MVC / Blazor, etc.

In other words, for Web Forms projects, whether there is a responsibility-decomposition plan matters more than the runtime migration itself.

Web Forms starts from decomposing responsibilitiesA diagram showing that because Web Forms is not the same app model as ASP.NET Core, you start by separating screen logic from business logic, moving the logic out into a shared library, and rebuilding the UI on a different model.Assuming the assets carry over as isWeb Forms screen assetsSeparate screen logic from business logicMove the logic out into a shared libraryRebuild the UI on a different modelThe estimate falls apart

Figure 17: Web Forms is not an asset migration; expect to decompose the responsibilities and rebuild on another model.

5.6 Treat WCF Clients and WCF Servers Separately

Do not lump these together.

WCF clients

The WCF Client has supported NuGet packages for modern .NET. So if you only call WCF, the work can be lighter than it looks.

WCF servers

Hosting WCF services, on the other hand, is a different story. Microsoft’s guidance presents two broad modernization paths:

  • Use CoreWCF to preserve compatibility with existing clients
  • Move to modern RPC / HTTP-based stacks such as gRPC

Note that CoreWCF is a subset; it does not bring over all of WCF as is. It is suited to preserving compatibility with existing clients, but code changes and testing are a given.

Evaluate WCF separately on the client and server sidesA diagram showing that WCF clients have supported packages for modern .NET and can be lighter than they look, while the server side requires choosing between CoreWCF for compatibility and a redesign around gRPC or HTTP APIs.ClientServerPreserve compatibilityRedesignWhich side of WCF do you useMigrate with the supported packagesChoose a pathCoreWCF (a subset)gRPC / HTTP API

Figure 18: Calling and hosting differ in both difficulty and options, so do not estimate them as one.

6. Flush Out Technologies That Do Not Work on .NET, or Get Stuck Easily

This is something you absolutely want to do before starting. Microsoft maintains a list of technologies available on .NET Framework that are unavailable on .NET 6+.

6.1 Technologies That Tend to Be Red Lights

Technology Status on .NET How to think about it
AppDomain creation such as AppDomain.CreateDomain Unsupported Think of isolation via separate processes / containers / AssemblyLoadContext
.NET Remoting Unsupported Redesign around IPC, HTTP, gRPC, sockets, pipes
CAS / Security Transparency Unsupported as a security boundary Think in terms of OS / container / privilege separation
System.EnterpriseServices (COM+) Unsupported Isolate and replace COM+-based designs
Workflow Foundation Unsupported Estimate separately, including alternatives such as CoreWF
WCF server Not available as is in the box Choose CoreWCF or gRPC
BinaryFormatter On .NET 9+ the implementation always throws Migrate serializers; audit ResX / clipboard / drag & drop

6.2 AppDomain: “Some APIs Remain, but Creation Is a Different Matter”

The AppDomain area is a bit tricky. Some of the API surface remains on .NET, but the usage pattern of creating a new AppDomain for isolation is not supported.

So if you used AppDomains for the following purposes, a redesign is needed:

  • Plugin isolation
  • Unloading dynamically loaded code
  • Isolating partially trusted code
  • Temporary separation of execution environments

What you should look at before migrating is not just whether the word AppDomain appears, but what the AppDomain was being used for.

What you did with AppDomain changes the verdictA diagram showing that because creating a new AppDomain for isolation is unsupported even though some APIs remain, you distinguish between merely using the type and using it for isolation, and redesign around a separate process or AssemblyLoadContext in the latter case.Only the type or its info is referencedIt created domains for isolationAppDomain shows upIt often carries over as isA redesign is neededSeparate process / container / AssemblyLoadContext

Figure 19: What decides whether a redesign is needed is not the keyword but what it was used for.

6.3 Remoting Is “Deeper Than It Looks”

Remoting itself, obviously, but asynchronous delegate calls like BeginInvoke() / EndInvoke() on delegates can also fall into the affected area. This is not Remoting per se, but it is unsupported on modern .NET, so it needs to be flushed out before migrating.

So when searching, it is safer to look at this set together:

  • System.Runtime.Remoting
  • MarshalByRefObject
  • RealProxy
  • BeginInvoke( / EndInvoke(

6.4 BinaryFormatter Suddenly Comes to the Fore Depending on the Target Version

The older the codebase, the more likely BinaryFormatter is being used “without awareness.”

  • Persisted data
  • Caches
  • Session storage
  • Plugin state
  • Clipboard / drag & drop
  • ResX
  • WinForms / WPF designer serialization

On .NET 9 and later, BinaryFormatter’s implementation is not included in the runtime and the API always throws PlatformNotSupportedException. So this is not a “think about it later” item; it is an audit item the moment you decide the target version.

How BinaryFormatter comes to the foreA diagram showing that BinaryFormatter tends to be used without awareness in persisted data, ResX, and the clipboard, and because the API always throws on .NET 9 and later it becomes an audit item the moment the target version is decided.Unaware usage such as ResX and clipboardSet the target to .NET 9 or laterThe API always throwsAudit it as soon as that is decided

Figure 20: BinaryFormatter starts to bite the moment you pick the target, so audit it up front instead of deferring it.

6.5 Search Terms to Grep for First

Before starting, just running searches over the whole solution with the following terms changes the picture considerably.

System.Web
HttpContext.Current
System.Runtime.Remoting
MarshalByRefObject
AppDomain
BinaryFormatter
ServiceHost
ChannelFactory
System.EnterpriseServices
Workflow
packages.config
web.config.install.xdt
install.ps1
DllImport
AxInterop
Microsoft.Office.Interop

Finding even one of these does not mean instant failure. It is a map for knowing which parts can migrate on the standard route and which become a separate track.

The actual search commands

The tool can be rg (ripgrep) or PowerShell; either is fine.

# PowerShell. Search everything under the solution in one pass
Get-ChildItem -Recurse -Include *.cs,*.vb,*.config,*.csproj,*.vbproj |
    Select-String -Pattern 'BinaryFormatter|System\.Runtime\.Remoting|MarshalByRefObject|AppDomain\.CreateDomain' |
    Select-Object Path, LineNumber, Line
# ripgrep. When you want a rough count first
rg -n --stats "BinaryFormatter|System\.Runtime\.Remoting|AppDomain\.CreateDomain" --glob "*.cs" --glob "*.vb"

What to do once you find something

The search is the entry point; the judgment that follows is the real work. For each term, the next place to look is already determined.

Term found What to check next Where it lands
System.Web / HttpContext.Current Whether it can be pushed out of the shared library. Whether the caller can repackage it into DTOs Peel it off with the strategies in 8.5
System.Runtime.Remoting / MarshalByRefObject / BeginInvoke What the remote call was for: cross-process, cross-machine, or just an asynchronous call Redesign around IPC, HTTP, gRPC, or Task
AppDomain Whether it only uses the type name, or isolates with CreateDomain If it is for isolation, a separate process or AssemblyLoadContext (6.2)
BinaryFormatter Whether the written data has to be read back later. Whether it is used indirectly through ResX or the clipboard Move to a different serializer and plan how to reread the existing data (6.4)
ServiceHost / ChannelFactory Host side or client side Evaluate them separately, as in 5.6
packages.config / install.ps1 / web.config.install.xdt What was being rewritten at install time The landmines in 4.2. Hold the settings explicitly instead
DllImport / AxInterop / Microsoft.Office.Interop The bitness assumption, and the full set of files needed at runtime On to the bitness check in 9.4

Take it this far and you no longer have a question of “can we migrate” but a list of which parts follow the standard route and which need a separate estimate. That list is exactly what you want before starting.

From search to a listA diagram showing that searching is only the entry point and that following the next thing to check for each term turns the results into a list of what migrates on the standard route and what needs a separate estimate.Search the solution for the termsFollow the next check for each termIt becomes a list of standard route versus separate estimateA hit does not mean instant failure

Figure 21: The search itself is only the entry point; carry it through to judgment and it becomes the list you want before starting.

7. Decide How Much Windows-Only Dependency to Accept

A common misunderstanding in migrations is “once we are on .NET, we are cross-platform.” There is no such magic. If the app is deeply tied to Windows, it remains Windows-only after the migration, plainly.

7.1 Migrating While Staying Windows-Only Is Entirely Realistic

Microsoft provides the Windows Compatibility Pack, a way to use many Windows-oriented APIs from modern .NET: the registry, WMI, EventLog, Windows services, Directory Services, and so on.

Its existence matters a lot in practice.

  • We want to move to modern .NET first
  • But we are not leaving Windows for now
  • So we want to accept the Windows API dependencies for the time being

For teams in that situation, it is a strong option.

The first goal of a migration does not have to be going cross-platform.

7.2 But Windows-Only APIs Are Also “Debt That Bites Later”

The Windows Compatibility Pack existing does not make everything safe.

  • You want to run in Linux containers
  • You want to run on Kubernetes
  • You want macOS / Linux developers running the same build
  • You want to reduce Windows VMs in the cloud someday

If you have goals like these, it is better to make the Windows API dependencies visible now.

Where the compatibility pack fits, and the debt it leavesA diagram showing that if you are not leaving Windows for now the Windows Compatibility Pack lets you accept Windows API dependencies and move to modern .NET, but that if containers or cloud are a future goal those dependencies are debt that bites later and should be made visible now.Stay on Windows for nowIf containers or cloud are the goalMove to modern .NET firstAccept the dependencies for now via the compatibility packThe dependencies are debt that bites laterMake them visible now

Figure 22: The compatibility pack is a realistic bridge, but the dependency turns into debt depending on your goal, so keep it visible.

7.3 System.Drawing.Common Is Especially Often Misunderstood

System.Drawing.Common is a Windows-only library on .NET 6 and later. If you have code using it for image processing or text rendering, you first need to decide which way to go.

  • Keep operating on Windows?
  • Or want it running on Linux / macOS in the future?

In the former case, leaving it as is for now can be fine. In the latter, a replacement such as SkiaSharp or ImageSharp needs to be in the migration plan from the start.

The System.Drawing.Common forkA diagram showing that because System.Drawing.Common is Windows-only from .NET 6 on, staying on Windows can leave it as is for now, while running on Linux or macOS requires putting a SkiaSharp or ImageSharp replacement in the plan from the start.Keep operating on WindowsAlso run on Linux / macOSYou use System.Drawing.CommonLeaving it as is can be fine for nowReplace with SkiaSharp / ImageSharpInclude it in the migration plan from the start

Figure 23: System.Drawing.Common is Windows-only, so how you treat it forks on the landing point from day one.

7.4 Typical Smells That Indicate Windows Lock-In

When references or APIs like the following are present, it is safer to estimate on the assumption of “migrating while staying Windows-only, at least at first.”

  • Microsoft.Win32.Registry
  • System.Management
  • System.Diagnostics.EventLog
  • System.ServiceProcess
  • System.DirectoryServices
  • System.Drawing
  • DllImport / P/Invoke
  • COM references
  • AxInterop.*
  • Microsoft.Office.Interop.*

8. How You Carve Out Shared Libraries Changes the Difficulty

For larger solutions, it is no exaggeration to say the success of the migration is determined by how you cut the shared libraries.

8.1 Classify First

Libraries are easiest to organize when split into three broad kinds.

  1. Pure business logic / domain logic
  2. Middle layers with some app model dependency
  3. Layers tightly coupled to UI / Web / Windows APIs

Of these, the first to migrate should be kind 1.

  • Calculations
  • Rule evaluation
  • DTOs / contracts
  • Domain services
  • Simple data transformations

If you can extract this cleanly, the difficulty drops sharply.

Three library categories and the order to move themA diagram showing that libraries split into pure business logic, middle layers with some app model dependency, and layers tightly coupled to UI or Windows APIs, and that pure business logic is the first to move.Move this firstNextLastPure business logicExtracting it cleanly lowers the difficultyMiddle layer closer to the app modelLayer tight to UI and Windows APIs

Figure 24: Sort libraries into three layers and rescue them starting from the pure logic.

8.2 netstandard2.0 Is Still a Valid Bridge

Per Microsoft’s guidance, for a shared library that must also coexist with the .NET Framework side, the baseline is to consider .NET Standard 2.0 first.

Two points matter here.

  • .NET Framework does not support .NET Standard 2.1
  • If you want a shared library referenced from both old and new, 2.0 tends to be the practical answer
netstandard2.0 as a bridgeA diagram showing that because .NET Framework does not support .NET Standard 2.1, .NET Standard 2.0 tends to be the practical answer for a shared library that must be referenced from both the old and the new side.Can reference itCan reference itFramework does not support it.NET Framework sidenetstandard2.0 shared librarymodern .NET sidenetstandard2.1

Figure 25: As a bridge referenced from both sides, 2.0 rather than 2.1 is the practical answer.

8.3 What Changes with netstandard2.0

Policy What changes Good fit Caveats
Convert to netstandard2.0 Easy to reference from both old and new Pure business logic, common contracts, utilities App-model-specific APIs cannot live here
Multi-target (e.g. net48;net10.0) Keep common code while holding per-environment differences Libraries with a few environment differences More conditional compilation and build management
Go straight to net10.0-only Cleanest in the long run New layers that do not need old/new coexistence Cannot be referenced from .NET Framework

8.4 Compatibility Mode Is Not a Cure-All

.NET Standard 2.0 has a compatibility mode that allows referencing .NET Framework libraries. However, this is not magic that makes everything work transparently.

For example, libraries that assume app-model-specific APIs like WPF are plainly difficult. In other words, even when you say “shared library,” it is important to narrow it down to the responsibilities that can truly be shared.

When incrementally migrating ASP.NET Framework, having shared libraries hang directly off HttpContext.Current or System.Web is quite painful.

The basic strategies at that point are one of these:

  • Push the System.Web dependency outside the interface
  • Change the code to receive HttpContext-derived information as DTOs
  • Use adapters during the transition period
  • If that still does not work, peel it off gradually with multi-targeting

8.6 Raise Libraries Leaf-First

The ASP.NET incremental migration guide explicitly says to upgrade supporting libraries postorder depth-first, that is, from the leaves up.

This is quite effective for general solutions too, not just web.

  • Because dependencies are upgraded first, the upper layers become easier to see through
  • Compatibility issues are easier to localize
  • Testing per library becomes easier
Upgrading leaf-firstA diagram showing that upgrading supporting libraries from the leaves of the dependency graph upward makes the upper layers easier to see through and keeps compatibility issues localized and testable per library.Upgrade the leaf libraries firstUpgrade the middle layers whose dependencies are doneFinally the upper application layerProblems stay localized and testable

Figure 26: Go leaf-first and the ground is already settled by the time you reach the upper layers.

9. Take Inventory of NuGet / External Dependencies / Third-Party Components

Do this sloppily and you suffer the worst pain in the second half of the migration.

9.1 Dependencies Are Easier to Organize in 4 Categories

  1. Public NuGet packages
  2. In-house private packages / internal libraries
  3. Local DLL references
  4. COM / ActiveX / native DLLs / SDKs

Looking only at category 1 is not enough. The really dangerous ones are 3 and 4.

Four dependency categories and their riskA diagram showing that dependencies split into public NuGet packages, in-house packages, local DLL references, and COM or ActiveX or native DLLs, that looking only at public NuGet is not enough, and that the genuinely dangerous ones are local DLLs and the COM and native side.Take inventory of the dependenciesPublic NuGet and in-house packagesLocal DLL referencesCOM / ActiveX / native DLLsThis is the really dangerous side

Figure 27: Counting public NuGet packages is not enough; local DLLs and the COM and native side are what to flush out first.

9.2 What to Verify for Each Dependency

For each dependency, verify at least this much:

  • Does it target modern .NET?
  • Does it support PackageReference?
  • Is it fine with SDK-style projects?
  • Any x86 / x64 / ARM64 constraints?
  • Does it depend on design-time tooling or Visual Studio extensions?
  • Does it assume install scripts / config transforms?
  • Is it still supported?

9.3 Estimate Third-Party UI / Reporting / Design-Time Components Separately

For WinForms / WPF / ASP.NET migrations, this area matters a lot.

  • Grids
  • Reporting engines
  • PDF output components
  • Charting components
  • Designer-integrated UI libraries
  • ActiveX wrappers

These involve design-time support, not just the runtime. If your migration estimate only checks “does it compile,” you will plainly miss these.

9.4 Always Check Native DLLs and Bitness

Even if things seemed to run as AnyCPU in the .NET Framework era, in reality they may depend on things like:

  • COM fixed to x86
  • 32-bit-only ActiveX
  • A specific version of the VC++ Runtime
  • Signed native DLLs

These are not problems that suddenly sprouted with the move to modern .NET; constraints that were always there simply surface. Which is exactly why it is worth making them visible before the migration.

Bitness constraints surfacingA diagram showing that code that appeared to run as AnyCPU may depend on x86-fixed COM, 32-bit-only ActiveX, or a specific VC++ Runtime version, and that the move to modern .NET merely surfaces constraints that were always there.It looked like it ran as AnyCPUDepends on x86-fixed COM, 32-bit ActiveX, and the likeThe migration surfaces the pre-existing constraintsMake them visible before migrating

Figure 28: The migration does not create the bitness problem; it only makes an existing constraint visible.

10. Treat EF6, Serializers, and the Data Layer as Separate Problems

The runtime migration and the redesign of data access and serialization work out better when treated as separate problems wherever possible.

10.1 EF6 to EF Core Is Not a Direct Upgrade

Microsoft’s EF guidance also states that EF Core is a total rewrite of EF6 and there is no direct upgrade path.

So for apps using EF6, this order is realistic:

  1. First move to modern .NET
  2. Keep EF6 if needed and run the app
  3. Then migrate to EF Core as a separate project

Do not combine the runtime migration with the ORM migration. That alone lowers the difficulty considerably.

Separating the EF6 and runtime migrationsA diagram showing that because EF Core is a total rewrite of EF6 with no direct upgrade path, the realistic order is to move to modern .NET first, keep EF6 running if needed, and migrate to EF Core later as a separate project.Do both at onceMove to modern .NET firstKeep EF6 running if neededMigrate to EF Core later as a separate projectORM behavior differences mix in and add weight

Figure 29: Do not move the runtime and the ORM at the same time; leaving EF6 in place while the runtime moves is the realistic order.

10.2 What Changes if You Keep EF6

  • Upsides
    • The data access layer diff can be deferred
    • You can focus on migrating business logic and the app model
    • “EF Core behavioral differences” do not get mixed in
  • Caveats
    • For new development, EF Core is the main line
    • EF6 Designer / EDMX usage patterns have separate constraints

10.3 For EDMX-Based EF6, Look All the Way to “Design Time”

The EF6 documentation states that the EF Designer is not directly supported in .NET / .NET Standard projects or SDK-style .NET Framework projects.

For EDMX-based apps, you need to consider these three points separately:

  • Does it run at runtime?
  • Can the Designer be used?
  • How will the generated code be handled?

If you use EDMX heavily, it is safer to put this in the estimate from the start.

10.4 BinaryFormatter and Custom Serialization Easily Become “Hidden Dependencies”

Serializers are easy to miss with code search alone.

  • Persistence formats
  • Messaging
  • Caches
  • Old WCF / SOAP contracts
  • ResX
  • Clipboard / drag & drop

This area also involves data compatibility. That is, you need to verify not just “does it build” but whether it can still read the old data.

11. Include Configuration, Distribution, Operations, and CI/CD in the Migration Scope

The migration target is not just source code.

11.1 Configuration Files

On the .NET Framework side, quite a lot can be riding in app.config / web.config.

  • Connection strings
  • Custom config sections
  • WCF endpoint configuration
  • Binding redirects
  • Diagnostics
  • Various ASP.NET settings
  • The results of transforms applied at package install time

On the modern .NET side, how configuration is held and loaded changes in places. So “configuration files later” is dangerous.

The first thing to do is an inventory of the configuration.

  • What lives in the configuration files
  • Which entries are required at app startup
  • Which are per-environment differences
  • Which were auto-injected by NuGet or installers

11.2 Distribution Method

Look at the distribution shape before starting too.

  • Hosted under IIS?
  • A Windows service?
  • A Scheduled Task?
  • ClickOnce / MSI / a custom installer?
  • On-premises servers assumed?
  • Is self-contained or framework-dependent the better fit?

Even if the executable itself migrates, you get stuck at the end if the distribution and startup machinery still assumes the old world.

11.3 Logging, Monitoring, and Operational Procedures

The operations side is easy to overlook as well.

  • Is the Windows Event Log assumed?
  • Are Performance Counters being watched?
  • WMI-based monitoring?
  • Are service accounts and permissions fixed?
  • Do logs assume local file output?

“The code runs after the migration, but operations cannot function” happens, plainly.

11.4 CI/CD and Build Agents

Before migrating, also check:

  • Can the required .NET SDK be installed on the build agents?
  • What to do with pipelines that assume nuget.exe / msbuild.exe
  • Will you move to the dotnet CLI?
  • How to update the test, coverage, and publish jobs
  • Are in-house templates or reusable pipelines assuming the old formats?

“It works on my machine but CI dies” is a migration classic.

Source code is not the only migration targetA diagram showing that even when the executable migrates, configuration files, the distribution method, logging and monitoring and operational procedures, and CI/CD with build agents will trip you up at the end if they still assume the old world, so they belong in the migration scope.Migrating the source codeThat is not the end of itConfiguration files and the distribution methodLogging, monitoring, operational proceduresCI/CD and build agentsIt works locally but CI dies

Figure 30: Only once configuration, distribution, operations, and CI/CD are included have you actually counted the migration scope.

12. A Realistic Way to Run the Migration

Given everything above, a realistic approach usually settles into roughly this shape.

12.1 First, Put the Current Framework Side in Order

  1. Move to .NET Framework 4.7.2 or later, ideally 4.8.1
  2. Upgrade dependencies
  3. Review packages.config
  4. Move to PackageReference and SDK style where possible
  5. Confirm the current app genuinely works in that state

Just doing this reduces the diff in the later phase considerably.

12.2 Rescue the Shared Libraries First

Next, move business logic and common contracts toward netstandard2.0 or multi-targeting. The order of upgrading is, as a rule, leaf-first.

12.3 For the Application Itself, Vary the Strategy per App Model

  • Class libraries / console / some services Relatively straightforward
  • WinForms / WPF Modernize while staying Windows-only
  • ASP.NET MVC / Web API All at once if small; incremental if heavy
  • Web Forms Assume screen replacement; move shared logic out first
  • WCF servers Decide first: stay with CoreWCF or redesign with gRPC

12.4 Stick to “Not Everything at Once”

The combinations to particularly avoid:

  • Runtime migration + full ORM swap
  • Runtime migration + authentication infrastructure change
  • Runtime migration + full cloud migration
  • Runtime migration + monitoring infrastructure change
  • Runtime migration + UI framework refresh

Even if all of them are needed, not stacking them in the same sprint usually works out better.

12.5 Take Tests and Baselines Before Moving

At minimum, you want this much in place before moving:

  • Unit tests
  • Integration tests for the main business flows
  • Snapshot-style verification of representative screens / APIs
  • A performance baseline
  • A way to check the main logs
  • A rollback procedure

Proceeding in a state where you cannot identify “what broke” after the migration is quite dangerous.

What to prepare before you moveA diagram showing that preparing tests, a performance baseline, a way to check the logs, and a rollback procedure before starting the migration lets you identify what broke afterward, and that proceeding without them is dangerous.Prepare tests and baselinesThen start the migrationYou can identify what brokeProceed without preparingBreakage cannot be identified, which is dangerous

Figure 31: The migration is decided by the preparation before you move; only a baseline lets you locate what broke.

13. Pre-Start Checklist

Here it is in a form you can paste straight into your project management tool.

13.1 Policy

  • You can state in one sentence why you are migrating
  • The landing point is decided: Windows-only modern .NET or future cross-platform
  • The target .NET version is decided
  • What is excluded from this scope (EF Core conversion, auth refresh, full cloud migration, etc.) is decided

13.2 Preparing the Current .NET Framework Side

  • Moved to .NET Framework 4.7.2 or later, ideally 4.8.1
  • Upgraded dependencies toward the latest
  • Checked for packages.config
  • Verified feasibility of converting to PackageReference
  • Verified feasibility of converting to SDK style
  • The current app builds, starts, and passes tests in that state

13.3 App Types and Technology Choices

  • Split difficulty by type: class library / desktop / web / WCF, etc.
  • Understood that WinForms / WPF remain Windows-only
  • Understood that ASP.NET Framework means an app model migration to ASP.NET Core
  • Included Web Forms UI layer replacement in the estimate
  • Evaluated WCF clients and servers separately

13.4 Unsupported Technologies / APIs Requiring Attention

  • Flushed out AppDomain dependencies
  • Flushed out Remoting / MarshalByRefObject / BeginInvoke / EndInvoke
  • Flushed out CAS / Security Transparency / COM+ / WF
  • Flushed out BinaryFormatter dependencies
  • Flushed out System.Web dependencies

13.5 Windows-Only Dependencies

  • Flushed out use of the registry, WMI, EventLog, Windows services, Directory Services
  • Flushed out use of System.Drawing.Common
  • Flushed out COM / ActiveX / Office Interop / P/Invoke / native DLLs
  • Verified x86 / x64 / ARM64 constraints

13.6 Shared Libraries and Data Access

  • Classified shared libraries into business logic / app-model-coupled layers
  • Flushed out what can be converted to netstandard2.0
  • Flushed out libraries that need multi-targeting
  • Decided whether the runtime can move first while keeping EF6
  • Verified EDMX / Designer dependencies

13.7 Operations and Build

  • Took inventory of the configuration files
  • Verified the distribution method (IIS / service / MSI / ClickOnce, etc.)
  • Verified logging / monitoring / permissions / execution account assumptions
  • Verified whether CI/CD and build agents need updating
  • Created a rollback procedure

14. Summary

What matters in migrating from .NET Framework to .NET is not “which command to migrate with” but seeing, before you start, what carries over as is and what is a separate problem.

Narrowed to the essentials, it is these six points:

  • Clean up the .NET Framework side before migrating
  • Split difficulty by app model
  • Flush out the unusable technologies first
  • Decide how much Windows-only dependency to accept
  • Decide how to carve out the shared libraries
  • Do not pile on ORM, authentication, and cloud migrations at the same time

The accuracy of the estimate changes dramatically in the first week of a migration. Put another way, if you can sort out the issues in that week, the second half can be steered fairly close to ordinary development.

How to use the first weekA diagram showing that sorting out in the first week what carries over as is and what is a separate problem raises the accuracy of the estimate and lets the second half be steered close to ordinary development.Sort out the issues in the first weekThe estimate gets more accurateThe second half moves closer to ordinary developmentSeparating what carries over from what is a separate problem

Figure 32: Whether you can spend the first week taking stock decides the accuracy of the whole migration.

“Let’s just try changing it to net10.0” is not bad as exploration. But as a production migration, there are things to look at before that. What this article organized is exactly that.

15. References

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Technical Consulting & Design Review

If you want to sort out the migration scope, how to slice an incremental migration, and how much Windows-only dependency to accept before starting, this topic works well as a technical consulting / design review engagement.

Frequently Asked Questions

Common questions about the topic of this article.

What should I do first before migrating from .NET Framework to .NET?
Finish cleaning up the .NET Framework side before you start implementing. Microsoft's official guide recommends upgrading to .NET Framework 4.7.2 or later (in practice, 4.8.1 where possible) before porting, converting packages.config to PackageReference, moving to the SDK-style project format, and updating dependencies toward their latest versions. Doing this first cuts the diff of the later move to modern .NET considerably, and it becomes much easier to isolate whether a compatibility problem comes from the age of the Framework or from the move to .NET. Take stock of unsupported technologies such as AppDomain, Remoting, and BinaryFormatter before starting as well.
Is deciding to stay on .NET Framework a realistic option?
Quite normally, yes. .NET Framework 4.8.1 stays supported as long as it runs on a supported version of Windows, so it is not the case that everything must move to modern .NET right now or you are immediately at risk. If you have a large stock of Web Forms screens, must preserve WCF server compatibility strictly, have deep Workflow Foundation or COM+ dependencies, or use third-party design-time components that do not support modern .NET, it is rational to consolidate on 4.8.1 for stable operation for now and plan the replacement on a separate track. The constraints do remain, though: you cannot get out of being Windows-only, and it is harder to benefit from the performance and language features of newer .NET.
Which technologies cannot move to .NET, or tend to get stuck?
AppDomain creation, .NET Remoting, CAS (Code Access Security), COM+ (System.EnterpriseServices), and Workflow Foundation are unsupported on modern .NET and are red lights that call for a redesign. WCF servers do not run as they are in the box, so you choose between CoreWCF and a redesign around gRPC or HTTP APIs. On .NET 9 and later, BinaryFormatter has no implementation in the runtime and always throws, so you need an audit that reaches persisted data, ResX, the clipboard, and drag & drop. Beyond those, install.ps1 and XDT transforms from packages.config, native DLLs, COM / ActiveX, and x86 assumptions are points to watch: the build can pass and the app can still fail at runtime.
Will migrating a WinForms or WPF app to .NET make it cross-platform?
No. WinForms and WPF can migrate to .NET, but they remain Windows-only frameworks. What the migration buys you is the modern .NET runtime, language features, SDK style, and better fit with CI/CD; it does not make the app run in a Linux container. If Linux or containers are a future goal, you need to inventory Windows-bound APIs such as System.Drawing.Common, the registry, WMI, EventLog, Windows services, COM, and Office Interop early. If you are staying Windows-only, on the other hand, you can take the pragmatic route of using the Windows Compatibility Pack to modernize the runtime first.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog