ASP.NET Framework Migration to ASP.NET Core with YARP

2022, Dec 20

Transitioning from ASP.NET Framework to ASP.NET Core was never simple, simply because of the way how ASP.NET Framework was created coupled to IIS, and how ASP.NET Core decoupled that dependency, introducing necessary breaking changes.

The migration

A new migration strategy is introduced with YARP. YARP is a reverse proxy used at Microsoft, that comes to bridge the challenging migration from ASP.NET Framework to ASP.NET Core.

Another reason this is now possible is because the new package Microsoft.AspNetCore.SystemAdapters.CoreServices introduce adapters to the System.Web library implementation of ASP.NET Framework, making it possible for ASP.NET Core to communicate with the legacy implementation.

Incremental changes allow the gradual transition of platforms, which was not possible before on ASP.NET.

Migration Strategy

where:

  • Façade - becomes YARP
  • Legacy - ASP.NET Framework
  • Modern - ASP.NET Core

How it works

Requests arrive on ASP.NET Core, that hosts YARP, which validates the request endpoint based on its configuration mapping.

If route mapping is found for YARP, it forwards the request to ASP.NET Framework, which resolves the request.

YARP-forward-ASPNET-Framework

If route mapping is not found for YARP, it then resolves the request on ASP.NET Core.

YARP-forward-ASPNET-Core

The example

An ASP.NET Framework 4.7.2 is used in this example, it contains a simple MVC page that makes a client JQuery AJAX request from the View to the Web API.

File: \Views\Dummy\Index.cshtml

<div class="row">
    <div class="col-md-12">
        <h2>This is a dummy example</h2>
        <ul id="dummy-list"></ul>
    </div>
</div>

@section scripts
{
    <script>
        $(document).ready(function () {
            $.ajax({
                url: "/api/values",
                contentType: "application/json",
                dataType: "json",
                success: function (data) {
                    $.each(data, function (index, value) {
                        $("#dummy-list").append("<li>" + value + "</li>");
                    })
                }
            });
        })
    </script>
}

File: \Controllers\ValuesController.cs

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "dummy1", "dummy2" };
    }
}

When it runs the following is displayed:

aspnet-framework-running

Migrating to ASP.NET Core

On Visual Studio 2022 install an Extension called Microsoft Project Migrations (Experimental).

extension

After installation, it introduces some menu items for Migration, which automates project provisioning within the solution. Choose to Migrate Project:

migrate-project

Let's start the migration, by creating a new project:

start-migration

new-project

The new project template to be created will be on .NET 7:

new-project-template

Note that for the new ASP.NET Core project, NuGet packages for YARP, System.Web adapters and YARP settings will be automatically added by the tool:

summary-changes

After the project creation, this is what is available on appsettings.json, containing the YARP settings:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ReverseProxy": {
    "Routes": {
      "fallbackRoute": {
        "ClusterId": "fallbackCluster",
        "Order": "2147483647",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "fallbackCluster": {
        "Destinations": {}
      }
    }
  }
}

and the ASP.NET Core project contains the following NuGet packages:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.SystemWebAdapters.CoreServices" Version="1.0.0" />
    <PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />
  </ItemGroup>

</Project>

The Program.cs initializes the ASP.NET Core. It is worth mentioning about the middlewares for SystemWebAdapters and ReverseProxy, injected to the Services:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSystemWebAdapters();
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();
app.UseAuthorization();
app.UseSystemWebAdapters();

app.MapDefaultControllerRoute();
app.MapReverseProxy();

app.Run();

Before we run it, let's migrate the ValuesController controller:

migrate-controller

migrate-values-controller

On the summary view of the controller, make sure to Migrate selection:

migrate-values-controller-summary

Note that the ValuesController is created on the new ASP.NET Core project.

To differentiate this ValuesController from the old one, modify the implementation to return a different string[] result:

File: \Controllers\ValuesController.cs

[ApiController]
  [Route("api/[controller]")]
  public class ValuesController : ControllerBase
  {
      [HttpGet]
      // GET api/values
      public IEnumerable<string> Get()
      {
          return new string[] { "core1", "core2" };
      }
  }

When it runs now, the following is displayed:

aspnet-core-running

This response comes from the new ASP.NET Core implementation, while the ASP.NET View is still coming from ASP.NET Framework. The gradual transition makes the migration painless, opening new possibilities for the migration of legacy solutions.

This example is available on PlayGoKids repository.