Using System.Web Adapters with VB.NET

July 10, 2023#Software Development
Article
Author image.

Sarah Dutkiewicz, Senior Trainer

We help some of our clients with migrating from .NET Framework to .NET Core apps. The .NET Upgrade Assistant can assist in the transition. One of the approaches we take is an incremental approach with the Strangler Fig pattern.

While going through documents on the incremental approach, you will see that they recommend using two tools:

  • YARP - a reverse proxy tool
  • System.Web Adapters - a collection of adapters to help with migrating from System.Web

We have been trying to use System.Web Adapters to share session information between VB.NET WebForms and .NET Core. Specifically, we are looking into reading the WebForms session information in our .NET Core app. We have run into a bunch of things that are in undocumented territory. So we figured we would share those findings here.

Setup

Our solution has 2 projects:

  • VB.NET WebForms application - standard VB.NET WebForms template
  • C# Razor application - standard C# Razor template

We are using YARP in the Razor application. It is set with the catch-all that if the Razor app doesn’t have the URL, it will fall back to the WebForms application. You can see this configuration in the appsettings.json in the .NET Core project.

We are taking the Remote app session state approach to the incremental migration.

When using System.Web Adapters, we’ve taken the approach they recommend with the strongly-typed shared session variables. We are using these session variables:

  • “UserName” of type string
  • “CurrentTime” of type DateTime

The VB.NET WebForms app will be the one hosting the session server used with System.Web Adapters.

Note: While this post doesn’t get into deploying to IIS, it is important to note that you may need to make sure both sites’ application pools are running with Integrated Managed Pipeline Mode, not Classic.

Screenshot of IIS Application Pools. The Managed Pipeline Mode column is highlighted, and the values for the application pools' pipeline modes are all set to Integrated.

VB.NET Guidance for System.Web Adapters

We have yet to encounter any documentation on moving from a VB.NET WebForms app to a C# .NET Core app using System.Web Adapters. So we have had to do a lot of translating between the languages.

One of the first things they have you do as part of an incremental migration is the remote app setup. This documentation is all in C#. Here’s what you need to know if you’re using VB.NET.

If you have a VB.NET WebForms application that you are trying to migrate to .NET Core and want to use System.Web Adapters, you can! You will need to do the following:

  1. Add NuGet references to Microsoft.AspNetCore.SystemWebAdapters, Microsoft.AspNetCore.SystemWebAdapters.Abstractions, and Microsoft.AspNetCore.SystemWebadapters.FrameworkServices.

  2. In Global.asax.vb, add the following block of code to the end of your Application_Start subroutine:

    With SystemWebAdapterConfiguration.AddSystemWebAdapters(Me)
        .AddJsonSessionSerializer(Sub(options)
                                    options.RegisterKey(Of String)("UserName")
                                    options.RegisterKey(Of DateTime)("CurrentTime")
                                  End Sub)
        .AddRemoteAppServer(Function(options)
                                options.ApiKey = ConfigurationManager.AppSettings("SessionServerApiKey")
                            End Function).AddSessionServer()
    End With

    Note: Do not move the AddSessionServer() to a new line. It needs to chain off of AddRemoteAppServer().

  3. In web.config, there are 2 changes that need to be checked:

    • Add an App Setting value named SessionServerApiKey with a random GUID for the key. It will look something like this:
    <appSettings>
        <add key="SessionServerApiKey" value="73d12d08-ee80-4424-a5ad-d18b04dfe318" />
    </appSettings>
    • Make sure there is an entry for the SystemWebAdapterModule in system.webServer/modules. It should look like this:
    <remove name="SystemWebAdapterModule" />
    <add name="SystemWebAdapterModule" type="Microsoft.AspNetCore.SystemWebAdapters.SystemWebAdapterModule, Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices" preCondition="managedHandler" />

Follow Microsoft’s guidance for the ASP.NET Core app setup. Remember to set the Remote App Client’s ApiKey to match whatever you used for the AddRemoteAppServer() call in the VB.NET WebForms app.

Links in the Demo App

In this demo app, we have the following pages linked in the navigation:

  • Inspect Session (WebForms) - This link will fail in the .NET Core routing and will be picked up by WebForms via the YARP routing. If you are running the .NET Core application on its own, this will fail.
  • Inspect Session (.NET Core) - This is the standard .NET Core session. When accessed via the .NET Core URL, it will get picked up properly. If you are running the WebForms application on its own, this will fail.
  • Inspect Session (Adapter) - This is the shared WebForms session using System.Web Adapters. It is hosted as an endpoint in .NET Core.

Run the App

To run this app, you need:

  1. Download or clone our sample repo.

  2. Open the solution file in Visual Studio. (We have created this using Visual Studio 2022.)

  3. Set both projects as Startup Projects. Right-click on the solution, then select Configure Startup Projects…. Select Start for both of the projects. Then select OK.

Screenshot of the Startup Project options in Visual Studio. "Multiple startup projects" is selected. VBMigrationDemo and DotNetCoreRazor are both set to "Start".

  1. When the .NET Core App loads, select the link for Inspect Session (WebForms). This takes you to the session management page (InspectSessionWebForms) in the WebForms project.

Screenshot of the top navigation with the "Inspect Session (WebForms)" link highlighted.

  1. Notice that this is showing a SessionId and no variables. Select the Update Session button to generate some values. When you select that button, it will add some values to session variables CurrentTime and UserName. It will look something like this:

Screenshot of the WebForms application, on the InspectSessionWebForms page, showing values in the CurrentTime and UserName session variables.

  1. From the WebForms navigation, select Inspect Session (.NET Core).This will take you to the session inspection page (InspectSessionCore) in the .NET Core app.

Screenshot of the WebForms application, on the InspectSessionWebForms page, showing values in the CurrentTime and UserName session variables.

Let’s look at this application further.

.NET Core Session

The .NET Core Session is looking specifically at the session in the Microsoft.AspNetCore.Mvc.RazorPages.HttpContext. This will iterate through any variables in the session and show their keys and string values. When this page is loaded, it will update the UserName variable with a different name.

Shared Session via System.Web Adapters

This is the whole reason why I was exploring this in the first place. I wanted to be able to read values from the WebForms’ session. System.Web Adapters allows for that to happen. How did I make it happen?

  • There is a static class named SharedSession. The GetSharedSessionObject static method is annotated with a Session annotation from SystemWebAdapters, which ensures we’re using the session from the session server. This annotation is important in order to ensure you are using the session from SystemWebAdapters. The documentation and videos linked at the end of this article include other ways to ensure this happens.

  • We pass the current context over to this static method using the HttpContext, following the guidance on Access HttpContext from Razor Pages.

You can tell this is the WebForms session being accessed in .NET Core based on the SessionID. The session ID for Step 5 above was from WebForms. The session ID for Step 6 above in the “Shared Session” section matches the Session ID in Step 5.

Calling /session with HttpClient

In this video Deep Dive into System.Web.Adapters [18 of 18] Migrating from ASP.NET to ASP.NET Core, Taylor creates an example where he maps a /session endpoint to return a string with some of the shared session values. I used that as a starting point. Initially, I had this returning a string. But then I wanted to work with it a bit more, so I used the static method for the SharedSession in that map.

You need to make sure to include .RequireSystemWebAdapterSession() off of the Map route so that it ensures that the session is being used from SystemWebAdapters.

To see what this endpoint returns, in the navigation of the .NET Core App, select the link for Inspect Session (Adapter).

Screenshot of the top navigation with the "Inspect Session (Adapter)" link highlighted.

If you open this in the same browser where you were looking at the .NET Core session, you’ll notice that the /session endpoint serves up the shared session. However, what if you tried accessing that endpoint with an HttpClient?

Notice in the screenshot for Step 6 that the Session ID is different for this endpoint. Keep in mind that a new HttpClient would have a different session than your own current session. That’s the nature of sessions.

Conclusion

In this example, we walked you through configuring a VB.NET WebForms application and .NET Razor application so that you can use YARP as the reverse proxy and use System.Web Adapters to read the WebForms session in .NET Core.

Additional Resources

These are some resources that helped me get through System.Web Adapters and understanding how they work:


Copyright © 2024 NimblePros - All Rights Reserved