Migrating ASP.NET MVC to .NET Core: A Practical Guide

If you’re still running ASP.NET MVC on .NET Framework, you’re on extended support only — no new features, security patches at best. Here’s a practical migration path based on real production upgrades, including the parts that actually cost time.

Why migrate

  • End of active development — .NET Framework is feature-frozen. All new investment from Microsoft goes into .NET Core / .NET 8+.
  • Performance — 2–3x throughput improvement on typical CRUD workloads is common, mostly from Kestrel and the rewritten middleware pipeline.
  • Cross-platform hosting — deploy on Linux containers instead of Windows/IIS licensing, which cuts hosting cost meaningfully at scale.
  • Built-in DI and better testability — no need for a bolted-on container; the framework assumes dependency injection from the start.

Framework vs. Core: what actually changes

Before the step-by-step, it helps to see where the architecture actually diverges. This is the part people underestimate — it’s not a recompile, it’s a different pipeline.

Migrating ASP.NET MVC to .NET Core

Pre-migration assessment

Before touching code, audit these three areas. This is where most timeline estimates go wrong.

01

NuGet package compatibility. Check .NET Standard / .NET Core support for every package you depend on. This is usually the biggest blocker — not your own code. A single unmaintained package with no Core-compatible fork can stall a migration for weeks.

02

System.Web dependencies. HttpContext.Current, in-process session state, and Web.config all need rework. There’s no direct 1:1 replacement — you’re re-architecting how request-scoped state flows through your app.

03

Authentication. Forms Authentication or an older Identity version needs a full rework toward ASP.NET Core Identity or a token-based (JWT/OIDC) approach. Don’t treat this as a config change — it’s a redesign.

Migration steps

  1. 1

    Target framework migration

    Convert your .csproj to SDK-style format. The try-convert tool handles simple projects; for anything with custom MSBuild targets, do it manually — the tool struggles there and you’ll spend longer debugging its output than doing it by hand.

  2. 2

    Replace System.Web

    Swap HttpContext.Current for injected IHttpContextAccessor. Move Web.config settings into appsettings.json with the Options pattern (IOptions<T>).

    // Framework
    var userId = HttpContext.Current.Session["UserId"];
    
    // Core — inject IHttpContextAccessor
    public MyService(IHttpContextAccessor accessor) => _accessor = accessor;
    var userId = _accessor.HttpContext?.Session.GetString("UserId");

  3. 3

    Controllers & routing

    Attribute routing carries over mostly unchanged. Watch for action filters that used System.Web.Mvc.Filters — the namespace and base classes changed — and any reflection-based logic that assumed .NET Framework assembly loading behavior.

  4. 4

    Dependency injection

    If you’re on Unity or Ninject, you can keep it via the ASP.NET Core DI adapter packages, or migrate to the built-in container. For most agency-scale projects, moving to built-in DI is less maintenance long-term — one less package to keep compatible with future .NET versions.

  5. 5

    Entity Framework 6 → EF Core

    Usually the highest-effort step. EF Core doesn’t support lazy loading by default (you opt in via proxies), some LINQ translations differ, and migrations have a different file structure. Complex navigation properties and multi-level includes are where this bites hardest — test query output, don’t assume parity.

Common pitfalls

Silent LINQ query changes. Some queries that worked in EF6 throw at runtime in EF Core — or worse, silently evaluate client-side instead of translating to SQL. Always check query logs after migration; don’t trust “it compiles.”

Globalization defaults differ between Framework and Core. If you support multiple locales, test date and number formatting explicitly — don’t assume the old behavior carries over.

Session state. If you relied on heavy in-process session, move to a distributed cache (Redis) during the migration, not after — retrofitting it later means touching the same code twice.

Testing strategy

Run the old and new versions side by side against the same database where possible. EF Core query translation differences won’t show up until runtime — a green build tells you nothing about query correctness. Log generated SQL in staging and diff it against the Framework version for your highest-traffic endpoints before cutting over.

Need help with your migration?

We’ve modernized legacy ASP.NET MVC systems to .NET Core for production applications — architecture through deployment. Let’s talk about your migration.

Get in touch →

Leave a Reply

Your email address will not be published.Required fields are marked *