CategoriesUncategorized

Shared Session Management for Classic ASP and .NET Core Integration

Organizations often face challenge of maintaining legacy Classic ASP applications while developing new features in .NET Core. Complete system migration is not always practical solution due to complexity, risk, and cost. In this article, we are going to explore how to implement distributed session management that allows both systems to work together effectively.

Understanding the Architecture

In typical scenario, legacy Classic ASP application serves as main website (e.g., www.company.com), while new .NET Core application runs on subdomain (e.g., app.company.com). Main challenge is maintaining user session across these separate applications. Solution is to implement centralized session management using shared database.

Database Design

Here is simple but effective database structure:

CREATE TABLE [dbo].[DistributedSessions]
(
    [SessionId] VARCHAR(50) PRIMARY KEY,
    [Username] NVARCHAR(100) NOT NULL,
    [UserData] NVARCHAR(MAX),
    [LastAccessed] DATETIME NOT NULL,
    [CreatedOn] DATETIME NOT NULL,
    [ExpiresOn] DATETIME NOT NULL
)

CREATE INDEX IX_Sessions_Username ON [dbo].[DistributedSessions] ([Username])
CREATE INDEX IX_Sessions_Expiration ON [dbo].[DistributedSessions] ([ExpiresOn])

Classic ASP Implementation

Here is helper class for Classic ASP (saved as session.asp):

<%
Class DistributedSession
    Private connString
    Private sessionId

    Private Sub Class_Initialize()
        connString = "your_connection_string_here"
        sessionId = Request.Cookies("DistSessionId")

        If sessionId = "" Then
            sessionId = CreateNewSession()
        End If
    End Sub

    Private Function CreateNewSession()
        ' Generate unique session ID
        Dim newId
        newId = Replace(CreateObject("Scriptlet.TypeLib").GUID, "-", "")

        ' Create cookie
        Response.Cookies("DistSessionId") = newId
        Response.Cookies("DistSessionId").Domain = ".company.com"

        CreateNewSession = newId
    End Function

    Public Function GetUserData()
        Dim conn, rs
        Set conn = CreateObject("ADODB.Connection")
        conn.Open connString

        Set rs = conn.Execute("SELECT UserData FROM DistributedSessions " & _
                            "WHERE SessionId = '" & sessionId & "' " & _
                            "AND ExpiresOn > GETDATE()")

        If Not rs.EOF Then
            GetUserData = rs("UserData")
        End If

        rs.Close
        conn.Close
    End Function

    Public Sub SetUserData(userData)
        Dim conn
        Set conn = CreateObject("ADODB.Connection")
        conn.Open connString

        conn.Execute "UPDATE DistributedSessions " & _
                    "SET UserData = '" & Replace(userData, "'", "''") & "', " & _
                    "LastAccessed = GETDATE() " & _
                    "WHERE SessionId = '" & sessionId & "'"

        conn.Close
    End Sub
End Class
%>

.NET Core Implementation

Create SessionService class in your .NET Core project:

public class DistributedSessionService
{
    private readonly string _connectionString;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public DistributedSessionService(
        IConfiguration configuration,
        IHttpContextAccessor httpContextAccessor)
    {
        _connectionString = configuration.GetConnectionString("SessionDb");
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<UserSessionData> GetUserDataAsync()
    {
        var sessionId = _httpContextAccessor.HttpContext.Request
            .Cookies["DistSessionId"];

        if (string.IsNullOrEmpty(sessionId))
            return null;

        using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        var userData = await connection.QueryFirstOrDefaultAsync<string>(
            @"SELECT UserData 
              FROM DistributedSessions 
              WHERE SessionId = @SessionId 
              AND ExpiresOn > GETDATE()",
            new { SessionId = sessionId });

        return userData != null 
            ? JsonSerializer.Deserialize<UserSessionData>(userData)
            : null;
    }

    public async Task SetUserDataAsync(UserSessionData userData)
    {
        var sessionId = _httpContextAccessor.HttpContext.Request
            .Cookies["DistSessionId"];

        using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        await connection.ExecuteAsync(
            @"UPDATE DistributedSessions 
              SET UserData = @UserData,
                  LastAccessed = GETDATE()
              WHERE SessionId = @SessionId",
            new { 
                SessionId = sessionId,
                UserData = JsonSerializer.Serialize(userData)
            });
    }
}

Implementation Benefits

This approach provides several important advantages:

  1. Cost Effective
    • Allows continued use of stable legacy system
    • Minimizes risk of full migration
    • Reduces immediate development costs
  2. Technical Benefits
    • Shared authentication state
    • Consistent user experience
    • Scalable solution
  3. Business Benefits
    • Gradual migration path
    • Maintain business operations
    • Lower training costs

Important Considerations

When implementing this solution, remember these points:

  1. Security
    • Use parameterized queries to prevent SQL injection
    • Implement proper session timeout
    • Encrypt sensitive session data
    • Use secure cookie settings
  2. Performance
    • Implement caching where appropriate
    • Create maintenance job to clean expired sessions
    • Monitor database growth
  3. Maintenance
    • Log session operations for troubleshooting
    • Monitor session timeouts
    • Regular cleanup of expired sessions

    Conclusion

    Distributed session management provides practical solution for organizations that need to maintain legacy Classic ASP applications while developing new features in .NET Core. This approach reduces migration risks and costs while providing consistent user experience.

    Implementation requires careful planning and consideration of security implications, but benefits often outweigh complexity of setup. Organizations can continue using valuable legacy systems while gradually transitioning to modern technology stack.

    CategoriesUncategorized

    Blazor Will Change the Game

    For many years, front-end development has been dominated by single-page applications (SPA) frameworks like React and Angular. These frameworks allow developers to create rich, dynamic web applications, but they come with one significant drawback: they require developers to use JavaScript (or TypeScript). At the same time, the backend often uses a completely different language such as C#, Java, or Python. This means organizations need separate teams or developers with expertise in both frontend and backend technologies.

    The Challenge of Separate Teams

    Having different languages for the frontend and backend creates several challenges. First, it adds complexity because teams must maintain two codebases written in different languages, each with its own set of libraries, frameworks, and design patterns. Secondly, this setup can make communication between frontend and backend teams difficult, slowing down development and leading to potential inconsistencies in how the two sides of the system work together.

    Maintaining a system built with two different technologies is also costly in the long term. Your organization not only needs to hire developers who specialize in both frontend (React, Angular) and backend (C#, Java), but you also have to deal with the overhead of keeping these two parts of your application in sync.

    Enter Blazor

    Blazor offers a solution to this problem by allowing developers to write the entire application—both the frontend and the backend—in C#. With Blazor, the need for JavaScript for frontend development is eliminated. This means that your team can now build everything from the user interface to the business logic in the same language.

    Lowering Cost of Ownership and Maintenance

    By using a single language for both sides of the application, Blazor significantly reduces the complexity of maintaining the system. Your organization no longer has to maintain two separate codebases, reducing the overall effort required to keep the system running smoothly. This also means fewer bugs, as developers will only have to focus on mastering one language, which lowers the risk of errors caused by switching between languages.

    In the long run, this translates to lower costs for maintaining and updating your system. Because there is less complexity, fewer issues arise, and the time needed to fix problems or implement updates is greatly reduced. Blazor simplifies development, resulting in faster turnaround times for bug fixes and new features.

    Simplifying Hiring

    Another advantage of Blazor is that it makes hiring much easier. With traditional setups, you often need to hire separate specialists for frontend and backend development. But with Blazor, your development team can work across the entire stack using C#. This reduces the need for multiple skill sets, making it easier to find qualified developers.

    Additionally, Blazor makes it easier to onboard new developers. Instead of learning multiple languages and frameworks, new team members can focus on mastering just one language, speeding up their ability to contribute to the project.

    Other Benefits of Blazor

    Blazor also provides other benefits. For example, it offers strong integration with the .NET ecosystem, meaning that developers can take advantage of familiar tools, libraries, and frameworks. Blazor applications can also run server-side or client-side, offering flexibility based on the needs of your application.

    Additionally, because Blazor is a .NET framework, it has the backing of Microsoft, which provides a strong level of support and a long-term commitment to the technology.

    Conclusion

    Blazor is a game-changer for organizations looking to simplify their development process and reduce costs. By using a single language, C#, for both frontend and backend development, Blazor lowers the cost of ownership and maintenance, simplifies hiring, and reduces complexity. For organizations already invested in the .NET ecosystem or looking for ways to streamline their development, Blazor is a smart choice for building modern web applications.

    Categoriesmigration

    Why I Advocate for Thoughtful, Well-Planned Legacy System Migrations

    When a company decides to migrate a legacy system to modern technology, it can feel urgent. The existing application might be outdated, inefficient, or at risk of becoming unsupported. There is often pressure to “just get it done” quickly. But let me be clear: I don’t believe in rushing a legacy migration. Here’s why:

    1. Legacy Systems are Complex

    Legacy systems are often the backbone of a business, running critical operations that have been fine-tuned for years, or even decades. These systems are complex, filled with layers of custom code, patches, and sometimes outdated documentation (if any exists at all). Migrating such systems is not like flipping a switch. It requires careful planning, thorough understanding, and attention to detail.

    Rushing a migration project increases the risk of not getting it right and of missing important elements. Key business logic might not be fully understood, or crucial data could be overlooked. This can result in an incomplete or buggy new system, which could lead to downtime, operational disruptions, or worse — loss of business.

    2. Quality Suffers

    Rushing forces migrator devs to cut corners. Testing, for instance, might be reduced or skipped altogether. In my experience, skipping proper testing is one of the worst things you can do in a migration project. With legacy systems, there are often many unknowns, and it’s impossible to predict all the possible issues that could arise in the new environment without adequate testing.

    Also, documentation is often neglected when rushing, leading to a lack of clarity about how the new system should be used, maintained, or updated in the future. Without thorough documentation, teams are left to guess, which increases the risk of future problems.

    3. Data Migration Risks

    Legacy systems often hold years, if not decades, of important data. Migrating this data is tricky and time-consuming. There’s a lot of risk involved, especially when dealing with custom databases or outdated formats. Rushing through data migration could result in data loss, corruption, or inconsistencies. And in today’s world, where data is one of the most valuable assets a company holds, these risks can be extremely costly.

    I’ve seen rushed migrations where critical customer or transaction data wasn’t properly handled, leading to a nightmare of trying to recover or rebuild lost records. This is avoidable with a careful, deliberate approach.

    4. You Miss Out on Strategic Planning

    A rushed migration often focuses too narrowly on just “getting off the old system” rather than strategically thinking about how to improve business processes with the new technology. A migration is an opportunity to streamline workflows, optimize performance, and adopt modern tools and practices.

    When you rush, you lose out on these opportunities. Instead of taking the time to analyze what’s working and what’s not, you end up duplicating old processes in the new system — including inefficiencies that should have been eliminated. Slowing down and planning allows you to implement a migration that not only replaces the old system but also improves how your business operates.

    Conclusion

    Legacy migrations are important, but they are also delicate and complex processes. Rushing through them leads to poor outcomes, from missed details to unplanned downtime to potentially lost data. I always advocate for a thoughtful, strategic migration that considers all aspects of the system — including the risks involved. In the long run, a well-planned migration saves time, money, and stress. And that’s why I don’t do rush legacy migrations.

    Categoriesclassicasp

    Classic ASP Page Lifecycle

    I’ve always found myself looking up the Page Lifecycle of frameworks. It is useful when debugging and adding new features. Classic ASP is no different. Once a request is identified by IIS (Internet Information Server) to be a “.asp” request, the specific page is identified and the following flow occurs.

    1. Initialization:
      The ASP engine initializes the objects we have come to love (or hate): Request, Response, Session, and Application.
    2. Script Execution:
      The server processes the ASP script from top to bottom. It executes server-side code within <% %> tags, such as VBScript or JScript, generating HTML or other output to send to the client. This is where we open/close databases and file resources.
    3. Response Generation:
      After the script has been executed, the HTML generated from it is sent sent to the Response object, which accumulates the content. Other things such as headers and cookies can be modified before the output goes out.
    4. Response Sent to Client:
      The ASP engine then sends the response to the client.
    5. End of Processing:
      Expensive server-side objects like Session, Application, Request, Response, and Server are released, and any resources used by the script are freed.

    In summary, the Classic ASP lifecycle is simple but a legacy dev needs a foundational understanding of it in order to work with legacy apps.

    Categorieswebforms

    How to Love Your WebForms App: Optimizations and Tricks

    WebForms may be seen as “old” by many, but for some of us, it is still a trusted and powerful tool that does the job. Whether you’re maintaining an existing WebForms app or simply prefer it, there are ways to optimize and improve its performance. Let me share a few tricks I’ve learned along the way.

    1. Use ViewState Wisely

    One of the common challenges in WebForms is managing the size of ViewState. While it can be useful for preserving data between postbacks, ViewState can easily get out of hand and slow down your application. Always ask yourself: “Do I really need ViewState for this control?”

    You can disable it by setting EnableViewState="false" for individual controls or pages where it isn’t necessary. For example, controls like labels don’t need ViewState enabled. By trimming unnecessary ViewState, you can reduce the load time and improve the user experience.

    2. Leverage Caching

    WebForms apps can benefit a lot from caching. By caching data that doesn’t change often, you can prevent repeated database calls, reducing server load and speeding up response times.

    In WebForms, you can use the OutputCache directive for caching the output of pages or controls. For example:

    <%@ OutputCache Duration="60" VaryByParam="None" %>

    This will cache the output of your page for 60 seconds, making it faster for users who visit multiple times during that period.

    3. Avoid Too Many Postbacks

    WebForms uses postbacks heavily, but too many can slow things down. Whenever possible, minimize postbacks by using AJAX controls or separating logic that doesn’t require a full page refresh. For example, using UpdatePanel can help load parts of a page without refreshing the entire thing.

    <asp:UpdatePanel runat="server">
    <ContentTemplate>
    <!-- Your content here -->
    </ContentTemplate>
    </asp:UpdatePanel>

    But remember, even UpdatePanel should be used with care, as it can still lead to unnecessary server load if not optimized properly.

    4. Optimize Your Database Calls

    Many WebForms apps rely on traditional ADO.NET for database access. While it works fine, sometimes inefficient queries can slow down your application. Always try to optimize your SQL queries, using indexing and avoiding fetching too much data. If possible, use stored procedures for better performance.

    5. Upgrade What You Can

    Even if you’re sticking with WebForms, it doesn’t mean you can’t take advantage of newer technologies. Consider upgrading your app to use .NET 4.8 for better performance, security, and compatibility. Additionally, you can introduce modern practices like Dependency Injection to make your code more maintainable.

    Conclusion

    Just because WebForms is older doesn’t mean it can’t be optimized to perform well in today’s web environment. By making some small tweaks and keeping an eye on performance, you can continue to get value out of your application. Remember, the key to loving your WebForms app is understanding where improvements can be made, even in the smallest details.

    Categoriesclassicaspmigration

    Legacy Systems Migration – Constraints

    Migrating a legacy system to newer technologies can be a complex and demanding task. It is a complex endeavor and involves numerous technical, organizational, and financial challenges. Each migration is unique in its own way–determined by the organization’s systems layout, culture and history. In this post, I’ll explore 10 constraints that organizations might face during this process.

    1. Compatibility with Existing Data

    Data migration is one of the most critical aspects of moving from a legacy system. Ensuring that data is accurately transferred while maintaining its integrity can be tricky. Sometimes, a migrator dev must consider not only the process but also the differences in data structure and format of the legacy and new systems.

    2. Dependency on Outdated Libraries or Frameworks

    Many legacy systems rely on outdated libraries or frameworks that are no longer maintained. These dependencies may have security vulnerabilities or compatibility issues with modern platforms. Refactoring or replacing these components often requires careful planning and significant development effort.

    3. Limited Documentation

    Legacy systems often suffer from poor documentation. This can make it difficult for the migrator devs to understand how the system works or identify areas for improvement. This is especially the case when the original devs and product managers have long left the organization. This scenario means developers must rely on trial and error, which can slow down the migration process to a significant degree.

    4. Integration with External Systems

    Legacy systems rarely operate in isolation. They may have critical dependencies on external services, APIs, or other systems that need to be integrated into the new technology stack. Some of these external systems may also be outdated, making integration a more complex process.

    5. User Resistance

    A successful migration involves not just technical updates but also consideration for the end users. Employees or customers who have been using the legacy system for years may resist new workflows, designs, or interfaces. Managing this transition effectively requires careful communication and often a phased approach.

    6. Budget Constraints

    Migrating a legacy system can be expensive. Migrator devs often have to make hard choices about what features or parts of the system can be modernized within the available budget. Limited financial resources might restrict the scope of the migration, leading to technical debt being carried forward.

    7. Downtime and Operational Disruption

    Minimizing downtime during migration is critical, especially for systems that are in constant use. Migrator devs need to carefully plan the migration to avoid disrupting business operations, which often involves complex testing, scheduling, and fallback strategies in case of failure.

    8. Backward Compatibility Requirements

    In some cases, parts of the legacy system might need to run alongside the new system for an extended period. This creates a need for backward compatibility, requiring migrator devs to ensure the new system can still communicate with the older components until a full migration is complete.

    9. Security Risks

    Legacy systems often have outdated security practices or known vulnerabilities that must be addressed during migration. Upgrading to a modern technology stack offers an opportunity to implement better security standards, but the process of doing so can introduce new risks, requiring extensive testing.

    10. Skill Gaps in the Team

    One of the biggest constraints during a migration project can be the team’s lack of familiarity with the legacy system or the new technologies being adopted. Bridging this skill gap might require additional training, hiring new team members, or bringing in consultants, all of which add time and cost to the project. You will need people who have worked on legacy systems and have done at least one migration, like myself. If you would be interested in hiring me, please go to the “Contact” page.

    Migrating a legacy system is more than just a technical challenge—it’s a balancing act that involves navigating through constraints while making strategic decisions. By anticipating these constraints, developers can better plan and execute a successful migration.