Migrating to ASP.NET Core

This section documents ASP.NET MVC 5.3.x, ASP.NET Web API 2.2, ASP.NET Web Pages 3, OWIN/Katana, SignalR 2, and ASP.NET Identity 2 — all running on .NET Framework 4.8.1 — the System.Web-hosted MVC framework, its routing, Razor views, HTML helpers, model binding, filters, and the OWIN-based authentication/Identity stack — as described by the official documentation at Microsoft Learn (plus Web API, Web Pages, SignalR, and Identity), which are the reference these pages are written and verified against.

This is not ASP.NET Core MVC. ASP.NET MVC 5 is the pre-Core, System.Web-hosted MVC framework; it is functionally frozen and receives only security fixes. For the current, cross-platform MVC framework see MVC Controllers and Views under ASP.NET Core (Blazor).

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page documents migrating from ASP.NET MVC 5.3.x on .NET Framework 4.8.1 to ASP.NET Core. It is referenced from Choosing an ASP.NET Framework, which covers when to migrate at all; this page covers the concrete mechanics once that decision is made.

The concept-to-concept mapping

MVC 5 ASP.NET Core

Global.asax + App_Start/*.cs

Program.cs (minimal hosting model) — see Getting Started with ASP.NET Core

IDependencyResolver / third-party container (Ninject, Autofac)

Built-in IServiceCollection/DI — see Dependency Injection

HttpContextBase

HttpContext (obtained via DI; IHttpContextAccessor only where DI genuinely can’t reach)

ActionResult

IActionResult / IResult — see Controllers and Actions for the MVC 5 side

HTML helpers (Html.TextBoxFor, …​)

Tag Helpers (<input asp-for="…​" />) — see HTML Helpers and Forms and Razor Syntax and Tag Helpers

System.Web.Optimization (BundleConfig)

MapStaticAssets (.NET 9+) or an external bundler (Vite, webpack) — see Bundling and Client-Side Integration

OWIN middleware (IAppBuilder, Startup.Auth.cs)

ASP.NET Core middleware (IApplicationBuilder, app.Use(…​)) — see Authentication, Identity, and OWIN

ApiController (System.Web.Http)

[ApiController] on a regular ControllerBase in the same unified pipeline as MVC — see ASP.NET Web API 2

AuthorizeAttribute (roles only, mostly)

Policy-based authorization (AddAuthorizationBuilder, IAuthorizationRequirement) — see Security Hardening

web.config (appSettings, connectionStrings)

appsettings.json + appsettings.{Environment}.json
environment variables — see Configuration and the Options Pattern

Web API 2 filters (System.Web.Http.Filters)

ASP.NET Core filters (IActionFilter etc., one unified hierarchy for MVC and API alike) — see Filters

The ASPX view engine problem

Applications still carrying .aspx/.ascx views (the legacy WebFormViewEngine, superseded by Razor in MVC 3 — see Razor Syntax) have no automated conversion path: ASP.NET Core has no ASPX view engine at all, and the WebForms control-tree model (postbacks, ViewState, server controls) has no Core equivalent whatsoever. Every .aspx view must be hand-rewritten as Razor (or a Blazor component) before the containing application can move — this is frequently the single largest line item in an MVC 5 migration estimate, and worth converting to .cshtml before starting the Core migration itself, as an independent, lower-risk step on .NET Framework.

Incremental migration with System.Web.Adapters + YARP

For an application too large to migrate in one release, Microsoft’s supported incremental path runs the legacy MVC 5 app and a new ASP.NET Core app side by side, routing traffic between them with YARP (Yet Another Reverse Proxy) and sharing session/authentication state via System.Web.Adapters:

// In the new ASP.NET Core app
builder.Services.AddSystemWebAdapters()
    .AddJsonSessionSerializer(options => options.RegisterKey<int>("UserId"))
    .AddRemoteAppClient(options =>
    {
        options.RemoteAppUrl = new Uri("https://legacy.example.com/");
        options.ApiKey = builder.Configuration["RemoteAppApiKey"];
    });

app.UseSystemWebAdapters();

Requests are cut over route by route (YARP forwards unmigrated routes to the legacy MVC 5 app, migrated routes to the new ASP.NET Core app), while System.Web.Adapters keeps HttpContext/session data readable from both sides during the transition. See Incremental ASP.NET to ASP.NET Core migration.

The .NET Upgrade Assistant

For applications with limited System.Web-only surface, the .NET Upgrade Assistant (dotnet tool install -g upgrade-assistant) automates much of the mechanical conversion: project-file format (from packages.config/old-style .csproj to SDK-style), common namespace/API substitutions, and web.configappsettings.json scaffolding. It does not migrate ASPX views, OWIN middleware bodies, or business logic — it removes boilerplate so a developer’s remaining work is the semantic changes below, not the project-file mechanics.

upgrade-assistant upgrade MyApp.sln

What changes semantically (not just mechanically)

  • Sync-over-async — MVC 5 code that blocks on .Result/.Wait() (see Caching and Performance) must become genuinely async/await; ASP.NET Core’s Kestrel/thread-pool model punishes blocking calls even more visibly than IIS did.

  • HttpContext.Current — has no equivalent; every read must be replaced with an injected HttpContext (via a constructor parameter or IHttpContextAccessor), which means any static helper or singleton service that reached for HttpContext.Current needs its call sites reworked, not just recompiled.

  • TempData providers — MVC 5’s SessionStateTempDataProvider (see Controllers and Actions) becomes ASP.NET Core’s cookie-based TempData provider by default; the one-read semantics are preserved but the storage mechanism, and therefore its size limits and farm behavior, differ.

  • Model-binding differences — ASP.NET Core’s model binder unifies what MVC 5 split across MVC’s and Web API’s separate binders (see Model Binding and Validation and ASP.NET Web API 2); binding source attributes ([FromBody], [FromRoute], [FromQuery]) become explicit rather than inferred from convention plus provider order, which occasionally changes what actually binds for an ambiguous parameter.

  • Filters unify into one pipeline (MVC and Web API filters merge — see the mapping table above), so an application relying on the two filter hierarchies behaving differently needs that logic reconciled.

See ASP.NET to ASP.NET Core migration and Migrate from ASP.NET MVC to ASP.NET Core MVC for Microsoft’s own step-by-step guidance underlying this page.

See also the section landing page for every MVC 5 topic this migration touches, and Choosing an ASP.NET Framework for the decision of whether and when to migrate in the first place.