.NET on AWS Blog

Modernize ASP.NET Web Forms applications in-place using DotVVM

This post was co-authored by Alok Bhatnagar, Technical Account Manager, AWS, and Tomáš Herceg, founder of DotVVM, an open-source framework that simplifies creating line-of-business web applications for .NET developers. 

Since the genesis of the .NET Framework, ASP.NET Web Forms has been a popular choice for building websites. Even to this day, there are thousands of applications written using this programming model, sometimes running on unsupported versions of .NET Framework.

To extend their relevancy and tap into the benefits of .NET 8+, these applications need to be modernized. Some of these benefits include .NET’s ever evolving performance improvements, platform architecture improvements (such as new extensible hosting model, interfaces for configuration, logging, or dependency injection), and the option to deploy .NET workloads on Linux or in Linux containers.

The modernization journey, however, may include a number of other activities, as ASP.NET Web Forms is not compatible with ASP.NET Core.

One way to simplify this process is to use a UI framework which supports the new .NET 8+ as well as the old .NET Framework. This enables you to do the migration in-place and use the new library while not breaking the existing pages. To make this happen, you can use DotVVM, an open-source MVVM (Model-View-ViewModel) framework (a member of the .NET Foundation) that separates the application’s business logic from the user interface (UI), making it completely independent.

In this post, I’ll walk you through migrating a sample application from ASP.NET to ASP.NET Core using DotVVM.

Figure 1: Comparison of two ways of incremental modernization of .NET applications

Figure 1: Comparison of two ways of incremental modernization of .NET applications

Modernization using DotVVM

In-place modernization using DotVVM comprises the following activities:

  1. Prepare the application. Upgrade to .NET Framework 4.7.2 or newer and install DotVVM.Owin NuGet packages (OWIN is used by DotVVM internally).
  2. Rewrite pages and components one by one. All .master, .aspx, and .ascx files are transformed to DotVVM syntax, and their code-behind files are refactored into DotVVM ViewModels. This step may take a long time, but the application remains working and can be deployed at any moment. A good starting point is to use ASPX to DotVVM converter which helps with typical transformations. The remaining changes in code need to be done manually. The main differences between ASP.NET Web Forms and DotVVM are described in the cheat sheet for ASP.NET Developers.
  3. Remove the remaining .NET Framework artifacts. Upgrade technologies like WCF or old SignalR to their new equivalents and remove all references to System.Web from the code.
  4. Change the target framework to .NET 8.

Example Migration

You can find an example application of this modernization method in the dotvvm-samples-aws-webforms GitHub repository. The sample is a joke database application, composed of multiple branches:

  • The main branch contains the original application using ASP.NET Web Forms and .NET Framework 4.5.1.
  • step01 branch shows the progress after installing DotVVM NuGet packages.
  • step02 branch shows the migration of the Web Forms .master page.
  • step03 branch shows the migration of the first Web Forms .aspx page.
  • step04 branch shows the migration of the other .aspx pages.
  • step05 branch shows the code after migration of EF 6 to EF Core 8, and migration of ASP.NET Universal Membership Providers to ASP.NET Core Identity.
  • step06 branch shows the migrated application running on .NET 8.

I’ll walk you through migrating the application in the steps that follow.

Step 1: Run the application locally

Refer to Step 1: Running the original .NET Framework application locally for detailed instructions.

  1. Install DotVVM. Install the DotVVM for Visual Studio extension, restart Visual Studio, and open the solution.
  2. Prepare the database. You need to initialize the SQL Server database. To do that, you need to update the connection string in app.config file in the Altairis.VtipBaze.Import project, and run it.
  3. Run the project. When the database is ready, you should be able to riun the project locally.
Figure 2: Home page of the VtipBaze application

Figure 2: Home page of the VtipBaze application

Step 2: Add DotVVM in the project

Refer to Step 2: Installing DotVVM in the project for detailed instructions.

  1. Upgrade to a supported version of .NET Framework. The lowest version of .NET Framework supported by DotVVM is 4.7.2. If you’re on an earlier version, you need to upgrade all projects in the solution to .NET Framework 4.7.2 or later.
  2. Add DotVVM in the project. Right-click on the Web Forms project and choose Add DotVVM from the context menu. This will install all necessary NuGet packages.
Figure 3 Adding DotVVM in the ASP.NET Web Forms project

Figure 3: Adding DotVVM in the ASP.NET Web Forms project

  1. Add DotVVM.Adapters.WebForms package. I recommend adding the DotVVM.Adapters.WebForms package as it contains several helpful components and extension methods.

The project still runs on the old .NET Framework, but is now prepared for modernization.

Step 3: Migrate the master page

Refer to Step 3: Migrate first DotVVM for detailed instructions.

This is where you start rewriting individual pages from ASP.NET Web Forms to DotVVM. After following the listed steps, you will get an empty DotVVM page running inside the old ASP.NET Web Forms application.

  1. Create a master page with converted syntax. First, copy the master page markup in the clipboard and paste it in the ASPX to DotVVM Converter. Apply all suggestions and paste the resulting code into a newly created DotVVM Master Page.
Figure 4 Using the ASPX to DotVVM Converter

Figure 4: Using the ASPX to DotVVM Converter

  1. Manual changes in the markup. The converter cannot do everything – you will need to make additional changes to the converted markup. For example, the LoginStatus control present in Web Forms is substituted for a DotVVM LinkButton control calling a SignOut method.

Every DotVVM page needs to have a viewmodel – a C# class which represents the state of the page and handles commands, for example button clicks. For this master page, it needs to handle the SignOut method. Since we are still on the .NET Framework, we call FormsAuthentication.SignOut(). In the final step, this will be replaced with ASP.NET Core Cookie Authentication.

  1. Add a test page. When the master page is migrated, you can add a test to make sure everything works. Now we have an empty DotVVM page running inside the old ASP.NET Web Forms application. All existing pages work without any changes.

Step 4: Migrate the first page

Refer to Step 4: Migrate first page for detailed instructions.

  1. Convert and edit the TagList page. Copy and paste the markup of the TagList.aspx page in the converter and apply the suggestions. Paste the result in the TagList.dothtml file and perform the remaining changes so the syntax will be DotVVM-compliant.
  2. Refactor the page viewmodel. The original page loads the data using Entity Framework. Copy and paste this logic in the TagListViewModel and call it in the OnPreRender phase. Since exposing Entity Framework in viewmodels is not a recommended practice, we modify the Entity Framework query to select TagListModel objects.
  3. Finishing touches. The last step is to register the DotVVM route in DotvvmStartup.cs file for the same URL as the Web Forms route currently uses: /tags. When both frameworks register the same route URL, DotVVM takes precedence.

After finishing this step, one out of the five pages is migrated to DotVVM. The remaining pages are still using Web Forms, but they work. The new page uses the same layout and CSS styles.

Step 5: Migrate remaining pages

Refer to Step 5: Migrate remaining pages for detailed instructions.

Using the same approach from the previous step, you need to migrate the remaining pages of the application.

  • Always start by pasting the Web Forms markup in the converter and applying all the suggestions.
  • Next, create a DotVVM page and use the converted markup. When you try to run the page, you will probably see some errors. These need to be fixed manually.
  • Copy over all logic from the code-behind file to the page’s viewmodel and perform some refactoring.
  • We also strongly recommend extracting all business logic to separate service classes and use the viewmodel just to call these services and deal with presentation-specific concerns (like localization, value formatting, manipulation with the UI state, and so forth). This will improve the overall maintainability of the application.
  • Wherever the EF entities are used in the page, they should be replaced with the model classes, same as in the previous step. A positive side-effect of this is improving the performance of the query as it does not need to load columns which are not used.
  • Do not forget to register the DotVVM routes in DotvvmStartup.
  • The last step is to convert ASP.NET Web Forms Generic Handler which generates an RSS feed with the jokes. It needs to be replaced with DotVVM presenter, a very similar concept – it lets you compose a custom HTTP response for a particular route.

Although all pages have now been migrated to DotVVM, there are still references to some old .NET Framework API which we will get rid of in the next step.

Step 6: Migrate the Entity Framework 6

Refer to Step 6: Switching the database to the new .NET for detailed instructions.

Entity Framework 6 is supported even in the newer versions of .NET. However, the application uses ASP.NET Universal Membership Providers which are not supported on modern .NET. We will replace it with ASP.NET Core Identity. Therefore, we need to migrate from EF6 to Entity Framework Core 8.

  1. Migrate to Entity Framework Core. First, you need to migrate Altairis.VtipBaze.Data and Altairis.VtipBaze.Import projects the new SDK-style format. This can be done using the .NET Migration Assistant extension.
  2. Migrate to ASP.NET Core Identity. After migration, we remove unnecessary NuGet packages, and add the Entity Framework 8 and ASP.NET Core Identity. Some changes are required in the VtipBazeContext class. An afflictive point is that EF Core uses different naming for M:N relationship tables, so this needs to be fixed in the OnModelCreating method.
  3. Create the migrations. To add the ASP.NET Core Identity tables to the database, we can generate a new Entity Framework Core migration. However, it needs to be manually updated because it adds tables which are already present in the database. You need to keep only tables and constraints related to ASP.NET Core Identity in the migration.
  4. Migrating the database seed tool. Altairis.VtipBaze.Import project is then updated to generate the admin user using the new ASP.NET Core Identity UserManager service. After running it, the new admin user will be ready in the database.

This is the only step after which the project cannot be compiled because we have not upgraded the last project in the solution to the newest .NET. This is the goal of the next step.

Step 7: Change target framework

Refer to Step 7: Switching the web application to the new .NET for detailed instructions.

  1. Switching the web application to the new .NET. The code base contains many files that are not needed for modern .NET. The entire Pages folder with all Web Forms pages can be removed–they have already been replaced by their DotVVM equivalents. You can also remove Web Forms JavaScript files, App_Themes, old generic handlers, web.config and Global.asax files.

After that, you’ll unload the project file and edit it, replacing its contents with the new SDK-style project syntax. The new file is much shorter, and the project now targets .NET 8.0.

When you load the project, there are just 10 compile errors.

      • Some are related to the Forms Authentication which needs to be replaced with ASP.NET Core Authentication.
      • Most errors are caused by the fact that Startup.cs using OWIN needs to be ported to ASP.NET Core Startup.cs file.
      • The last errors are because of subtle differences between EF and EF Core DbSet API.
  1. Fixing authorization rules from web.config. An important change that needs to be implemented is authorization for pages based on roles. It was done in web.config in the Web Forms, but this file is not present in ASP.NET Core. Instead, this check is done in DotVVM viewmodel by calling await Context.Authorize().
  2. Running the project. After clearing all compile errors, the application is ready to run on .NET 8. Since the new .NET supports Linux or containers, you can deploy it easily in AWS services like Amazon Elastic Container Service (Amazon ECS) or AWS App Runner.

Step 8: Migrate the DB to RDS SQL Server

You can migrate your self-managed database to a fully managed Amazon Relational Database Service (Amazon RDS) for SQL Server using the steps explained in Migrate Microsoft SQL Server to Amazon RDS.

Step 9: Deploy the app on AWS App Runner

To deploy the app on AWS App Runner, we will use the AWS Toolkit for Visual Studio and Visual Studio 2019/2022. Start by installing and configuring the toolkit, then complete the prerequisites for working with AWS App Runner. This includes providing the relevant AWS Identity and Access Management (IAM) permissions, VPC Connector for database and store connection string in AWS Secrets Manager, and confirming the specific source repository that you want to deploy.

To create an App Runner service:

  1. Open AWS Explorer in Visual Studio.
  2. Right-click the App Runner node and choose Create Service.
  3. On the Command Palette, for Select a source code location type, choose Repository.

To deploy the code from GitHub Repository:

  1. For Select a connection, choose a connection that links GitHub to AWS. The connections that are available for selection are listed on the GitHub connections page on the App Runner console.
  2. For Select a remote GitHub repository, where the DOTVVM re-factored source code resides.
  3. For Select a branch, choose the Git branch of your source code that you want to deploy.
  4. For Choose configuration source, specify the configurations as below-

If you choose Use configuration file, your service instance is configured by settings that are defined by the apprunner.yaml configuration file. This file is in the root directory of your application’s repository.

If you choose Configure all settings here, use the Command palette to specify the following:

  • Runtime: Choose .NET 8.
  • Build command: dotnet publish -c Release -o out
  • Start command: dotnet out/Altairis.VtipBaze.WebCore.dll.
  1. For Port, enter the IP port that’s used by the service (Port 8000, for example).
  2. For Configure environment variables, you can skip this step.
  3. For Name your service, enter a unique name without spaces and press Enter.
  4. For Select instance configuration, choose a combination of CPU units and memory in GB for your service instance.

When your service is being created, its status changes from Creating to Running.

  1. After your service starts running, right-click it and choose Copy Service URL.
  2. To access your deployed application, paste the copied URL into the address bar of your web browser.

Here’s what the transformed application looks like running on AWS:

Figure 5: Modernized DotVVM application running on AWS

Figure 5: Modernized .NET DotVVM application running on AWS

Congratulations! You’ve modernized an ASP.NET Web Forms application to cross-platform .NET 8 and are now running in on AWS on a Linux container.

Clean up

To avoid ongoing charges, delete the AWS resources created in this walkthrough. Refer to the following resources for instructions:

  1. Delete the App Runner service
  2. Delete the Amazon RDS resources

Conclusion

DotVVM enables you to modernize legacy ASP.NET Web Forms applications incrementally and do everything within the same solution. You do not need to set up a second application, configure YARP proxy, implement single sign-on experience and deal with concurrency issues caused by caching or session state sharing.

Many changes between the ASPX and DotVVM page markup can be done automatically by using ASPX to DotVVM converter. You must do the remaining changes in the pages manually, and you need to refactor the code-behind files into DotVVM ViewModels. However, most of the business logic stays untouched or with just minimal changes. This lowers the risk of introducing new bugs or unwanted functional changes.

Delivering business value by fully taking advantage of the AWS Cloud requires agile ways of working, flexible application architectures, and modern development practices. This blog serves as an option to containerize the legacy .NET web applications and host them on modern AWS services such as App Runner; the real value of running .NET applications on AWS is in integrating them with the growing platform of innovative AWS Services.

You can find more on Developing and Deploying .NET Applications on AWS in this paper.

You can find more resources in DotVVM Documentation. To get in touch with the DotVVM community to ask questions or provide feedback, visit the Support page.

The content and opinions in this blog are those of the third-party author and AWS is not responsible for the content or accuracy of this post.