Search  
Always will be ready notify the world about expectations as easy as possible: job change page
Nov 20, 2022

Modernizing ASP.NET Web Forms Applications (Part 3)

Author:
Tomáš Herceg
Source:
Views:
856

I wrote about replacing the authentication with OWIN Security libraries. In this part, I would like to introduce DotVVM – an open source framework which allows building web applications using the MVVM pattern and is very easy to use. I will also show how you can modernize old Web Forms applications using DotVVM.

I have started the DotVVM project when I noticed that Web Forms are not going to be supported on ASP.NET Core. I realized that there are thousands of companies still heavily relying on legacy Web Forms applications.

In many cases, it is just not possible to rewrite the applications from scratch. They have often been developed for more than 10 years and the idea of rewriting them in less than half of that time is ridiculous. Especially in the current world of over-complicated JavaScript frameworks which add tremendous amount of complexity and make almost impossible to be a full-stack web developer.

How does DotVVM work?

The idea behind DotVVM was quite simple – to build a successor of ASP.NET Web Forms, with support of .NET Core, and get rid of the biggest pain points Web Forms had.

No ViewState

Web Forms store state of all controls in the page in a cryptic hidden field which is called viewstate. Although it is possible to specify which controls can use it and which cannot, still, it is not possible to get rid of it completely, and some controls stop working without it.

In DotVVM, the controls in the page are stateless. The page is backed by a viewmodel, which is a C# class you need to write, and all the state is stored in the viewmodel instance. The purpose of viewmodel is to store page state and handle user actions (clicking a button for example).

You can make postbacks in DotVVM, same as in Web Forms. But you can control what will be transferred from the client to the server – instead of hundreds of kilobites of encrypted state, it is a plain JSON object which corresponds with the C# viewmodel. You can specify bind direction to particular properties, so they won’t be transferred in both ways.

And what’s best – for most actions, you don’t need to do a full postback. DotVVM 2.0 brought REST API bindings, which can significantly reduce the amount of data in the viewmodel, and Static Command Services, which can also avoid transferring the entire viewmodel to the server and back. And we are working on even more interesting things right now.

Clean HTML

Web Forms controls contain many properties to change how the controls look like – FontSize, BackColor and so on. These properties emit inline styles, which is considered as a bad practise, and the HTML rendered by some controls is difficult to style using CSS.

DotVVM relies on CSS. All controls produce as simple HTML as possible, and everything is easily styleable. You can specify which HTML elements should be produced by some controls (Repeater, for example), so you can use the control to render unordered lists, or just a bunch of divs. Also, we have documented what each control produces.

Testability & Better Architecture

The viewmodels in DotVVM don’t reference controls from the UI – actually, they even don’t know which control will use a particular property. Thanks to the data-binding, any change made to the properties will be reflected by the controls, and vice-versa. This makes the viewmodels testable – you can just create an instance of the viewmodel, set some properties, call a method and make sure it did what it should do.

Also, DotVVM natively supports dependency injection, action filters and many other concepts known from MVC or ASP.NET Core. It supports both old ASP.NET and new ASP.NET Core, and can be used together with any other framework in one application. This is super-interesting for the rest of this article. 

How can DotVVM help with existing Web Form applications?

If you need to extend Web Forms applications with new pages or areas, you should definitely consider writing these new parts in DotVVM. The syntax is cleaner, you can access your business layer the same way as in Web Forms, and finally, many things are easier to do in DotVVM.

If you know that you will need to run this application next five years or more, you may use DotVVM to modernize it, and possibly upgrade to .NET Core. You can take Web Forms pages and rewrite them one by one while keeping the entire application still working. After a while, when you get rid of all Web Forms pages, you might be able to upgrade the project to .NET Core (DotVVM supports both .NET Framework and .NET Core). DotVVM is a live project and is constantly adding new features, so your platform will be in sync with the world of web development which changes every year.

How to start?

I have started building a NuGet package called DotVVM.Adapters.WebForms. It will contain several controls that allow closer integration of the two technologies.

  • There are dotvvm:RouteLink and webforms:RouteLink controls, which can render a link to DotVVM or ASP.NET Web Forms route, which is helpful when making a transition between the two parts.
  • Currently, I am working on a control which can host DotVVM user control and its viewmodel in a Web Forms page. This is more difficult to do as it requires some changes in the framework itself, but still, it would be a nice step forward.

The project is hosted on GitHub and I am looking for a feedback from community – it will help us to choose the next steps and prioritize the ideas we have.

I have also created a DotVVM cheat sheet for Web Forms developers so you can see the differences between these two technologies.

Installing DotVVM package

To integrate DotVVM with Web Forms, you need to perform the following steps:

  1. Install DotVVM.Owin and Microsoft.Owin.Host.SystemWeb packages in the project.
     
  2. Open the csproj file in a text editor and add change the ProjectTypeGuids element to this:

    <ProjectTypeGuids>{94EE71E2-EE2A-480B-8704-AF46D2E58D94};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
     
  3. Add DotvvmStartup.cs file in the project. This file contains the configuration of DotVVM:

    public class DotvvmStartup : IDotvvmStartup, IDotvvmServiceConfigurator
    {
        public void Configure(DotvvmConfiguration config, string applicationPath)
        {
            config.AddWebFormsAdapterControls();

            RegisterRoutes(config, applicationPath);
            RegisterControls(config, applicationPath);
            RegisterResources(config, applicationPath);
        }

        private void RegisterRoutes(DotvvmConfiguration config, string applicationPath)
        {
            // ...
        }

        private void RegisterControls(DotvvmConfiguration config, string applicationPath)
        {
            
        }

        private void RegisterResources(DotvvmConfiguration config, string applicationPath)
        {
            
        }

        public void ConfigureServices(IDotvvmServiceCollection services)
        {
        }
    }
     
  4. Add Startup.cs in the project. This file contains the OWIN pipeline configuration. You may already have this file in the project, if you are using SignalR or another OWIN-based library.

    [assembly: OwinStartup(typeof(YourNamespace.Startup))]

    namespace YourNamespace
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                app.UseDotVVM<DotvvmStartup>(HostingEnvironment.ApplicationPhysicalPath);
            }
        }
    }
     
  5. Create Views and ViewModels folders somewhere in the project.
     
  6. Create a DotVVM Master Page and copy all contents of ASP.NET Web Forms Master Page. The goal is to make DotVVM page look exactly the same as the Web Forms one. You need to have two – one for Web Forms, one for DotVVM. It is not possible to reuse it.
     
  7. Replace all links (e.g. in the menu) with <webforms:RouteLink> controls.
     
  8. Replace <asp:ContentPlaceHolder> with <dot:ContentPlaceHolder>.

  9. Look for runat=”server” in all controls and replace all Web Forms controls with their DotVVM equivalents:
    1. There are no HyperLink, Label, Image, Panel controls in DotVVM – use their HTML equivalents.
    2. DotVVM doesn’t have controls like Menu or Calendar – you need to use HTML and CSS to make any menu you may need. We have the Calendar control in DotVVM Business Pack.
    3. Form controls (TextBox, CheckBox…), Repeater, FileUpload, GridView and many other controls have the same names.
    4. Replace all appearance properties (FontSize etc.) with proper CSS. I strongly recommend using LESS or SCSS.
    5. Replace all properties which are set from the code-behind, with data-binding, and place the corresponding properties in the master page viewmodel. If there are any values which cannot change while the page is loaded, you can use resource binding to render them directly in HTML.
    The migration shouldn't bee difficult - most of the master pages I have seen, are quite simple and use only basic controls.
     
  10. Start creating new pages in DotVVM using the master page. You can always make a link to a Web Forms page using <webforms:RouteLink> control, and you can always link to a DotVVM page using <dotvvm:RouteLink runat=”server”> control in a Web Forms page.
     
  11. Make sure the single-sign on works as expected. DotVVM should see the user identity even if you are using Forms Authentication. I strongly recommend to move to OWIN Security, which was described in the previous part.

 

Of course, there are so many Web Forms specific stuff that I couldn’t cover everything. However, I have tried to apply all these steps in 10 various Web Forms applications I made years ago, and I was able to integrate DotVVM very quickly.

If you try this approach, definitely let us know on our Gitter – we are very interested in any issues you might have run into. And if you have any ideas, what controls you’d like to have ported, we are listening.

Send message
Type
Email
Your name
*Message