Claude Skill

dotnet-legacy-aspnet

Maintain classic ASP.NET applications on .NET Framework, including Web Forms, older MVC, and legacy hosting patterns, while planning realistic modernization boundaries.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download postpartum-genushyacinthus29-dotnet-skills-skills_dotnet-legacy-aspnet-bfa4ebd.zip · 7 KB
Part of postpartum-genushyacinthus29/dotnet-skills — 80 skills

Install

skills CLI npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills/tree/main/skills/dotnet-legacy-aspnet
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install postpartum-genushyacinthus29-dotnet-skills@llmmart
Git git clone https://github.com/Postpartum-genushyacinthus29/dotnet-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole postpartum-genushyacinthus29/dotnet-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Legacy ASP.NET

Trigger On

  • working in Web Forms, legacy MVC, or classic ASP.NET applications
  • reviewing old IIS-centric configuration and lifecycle behavior
  • planning migration toward ASP.NET Core without breaking core business flows

Workflow

  1. Treat classic ASP.NET as a distinct stack with different hosting, lifecycle, and configuration rules from ASP.NET Core.
  2. Stabilize behavior first: routing, session, auth, server controls, configuration transforms, and deployment assumptions.
  3. Plan modernization in seams: isolate domain and service logic, then move replaceable edges instead of rewriting the whole app at once.
  4. Use dotnet-wcf or dotnet-entity-framework6 when the legacy app depends on those subsystems rather than flattening them into generic web work.
  5. Be careful with guidance copied from ASP.NET Core because middleware, DI, and hosting assumptions do not transfer directly.
  6. Validate in an environment that resembles real IIS and configuration transforms.

Deliver

  • practical maintenance guidance for classic ASP.NET
  • stabilized legacy behavior and modernization seams
  • a migration path that avoids unnecessary risk

Validate

  • classic and Core guidance are not mixed
  • legacy runtime assumptions are preserved deliberately
  • migration steps are incremental and testable

References

  • Migration Paths: strategies for migrating from ASP.NET to ASP.NET Core, including incremental migration, strangler fig pattern, and component-specific guidance
  • Maintenance Patterns: stabilization and maintenance patterns for legacy ASP.NET code, including abstraction layers, testing seams, and deployment practices
Files (dotnet-skills)
  • references
    • migration.md 6.5 KB
      # Migration Paths from ASP.NET to ASP.NET Core
      
      This document outlines practical migration strategies for moving classic ASP.NET applications to ASP.NET Core.
      
      ## Migration Strategy Selection
      
      ### Incremental Migration (Recommended for Large Applications)
      
      Use incremental migration when:
      - The application has significant business logic that must remain stable
      - Full rewrites carry unacceptable risk
      - The team needs to maintain production stability during migration
      
      Approach:
      1. Identify natural seams in the application architecture
      2. Extract shared business logic into .NET Standard libraries
      3. Create a new ASP.NET Core host alongside the legacy application
      4. Use YARP or a reverse proxy to route traffic between old and new endpoints
      5. Migrate one vertical slice at a time
      6. Decommission legacy endpoints as Core equivalents stabilize
      
      ### Strangler Fig Pattern
      
      Use the strangler fig approach when:
      - The application can tolerate running both stacks in parallel
      - You can route at the edge (load balancer, reverse proxy)
      - Migration will span multiple release cycles
      
      Implementation:
      1. Deploy an ASP.NET Core application alongside the legacy application
      2. Configure routing rules to send new or migrated endpoints to Core
      3. Gradually move functionality from legacy to Core
      4. Remove legacy application once all endpoints are migrated
      
      ### Full Rewrite
      
      Consider full rewrite only when:
      - The legacy application is small and well-understood
      - Business requirements have changed significantly
      - Technical debt makes incremental migration more expensive than rebuilding
      - The team has capacity for parallel development and testing
      
      ## Component Migration Paths
      
      ### Web Forms to ASP.NET Core
      
      Web Forms has no direct equivalent in ASP.NET Core. Migration options:
      
      1. **Razor Pages**: Best fit for page-centric workflows with code-behind patterns
         - Map ASPX pages to Razor Pages
         - Replace server controls with Tag Helpers or Razor components
         - Move code-behind logic to PageModel handlers
      
      2. **Blazor Server**: Best fit when preserving stateful component behavior
         - Recreate server controls as Blazor components
         - Maintain server-side rendering with interactive updates
         - Preserve familiar event-driven programming model
      
      3. **MVC**: Best fit when moving toward clean separation of concerns
         - Extract business logic into services
         - Map postback handlers to controller actions
         - Replace ViewState with explicit state management
      
      ### MVC 5 to ASP.NET Core MVC
      
      MVC migration is more straightforward but still requires attention:
      
      1. **Routing**: Replace RouteConfig with endpoint routing
         - `routes.MapRoute` becomes `endpoints.MapControllerRoute`
         - Attribute routing works similarly but uses different base classes
      
      2. **Filters**: Migrate action filters to ASP.NET Core filter contracts
         - `IActionFilter` interface signature changes
         - `FilterContext` replaces `ActionExecutingContext` patterns
      
      3. **Dependency Injection**: Move from third-party containers to built-in DI
         - Register services in `Program.cs` or `Startup.cs`
         - Replace service locator patterns with constructor injection
      
      4. **Configuration**: Replace Web.config with appsettings.json
         - Move connection strings to configuration providers
         - Replace ConfigurationManager calls with IConfiguration injection
      
      5. **Authentication**: Replace Forms Authentication or ASP.NET Identity
         - Cookie authentication middleware replaces FormsAuthentication
         - ASP.NET Core Identity replaces legacy ASP.NET Identity
      
      ### Session State
      
      1. **In-Process Session**: Direct migration to ASP.NET Core session middleware
      2. **SQL Server Session**: Use distributed session with SQL Server provider
      3. **Redis Session**: Use distributed session with Redis provider
      4. **Custom Providers**: Implement IDistributedCache or ITicketStore
      
      ### Caching
      
      1. **System.Web.Caching**: Replace with IMemoryCache or IDistributedCache
      2. **Output Caching**: Use Response Caching middleware
      3. **HttpRuntime.Cache**: Inject IMemoryCache instead
      
      ### HTTP Modules and Handlers
      
      1. **HTTP Modules**: Convert to middleware
         - `BeginRequest` maps to middleware pipeline position
         - `AuthenticateRequest` maps to authentication middleware
         - `AuthorizeRequest` maps to authorization middleware
      
      2. **HTTP Handlers**: Convert to middleware or endpoint handlers
         - ASHX handlers become middleware or minimal API endpoints
         - Map specific routes to handler logic
      
      ## Configuration Migration
      
      ### Web.config to appsettings.json
      
      | Web.config | ASP.NET Core |
      |------------|--------------|
      | `<connectionStrings>` | `ConnectionStrings` section in appsettings.json |
      | `<appSettings>` | Configuration sections or direct values |
      | `<system.web>` | Middleware and service configuration |
      | `<httpRuntime>` | Kestrel limits and middleware options |
      | `<customErrors>` | Exception handling middleware |
      | `<authorization>` | Authorization middleware and policies |
      
      ### Machine.config Dependencies
      
      Identify machine-level configuration dependencies:
      - Connection strings in machine.config
      - Custom configuration sections
      - GAC assemblies
      
      Migrate these to:
      - Environment variables or secure configuration providers
      - NuGet package references
      - Application-level configuration
      
      ## Testing Migration
      
      1. **Unit Tests**: Often portable with namespace changes
         - Update test framework packages to .NET-compatible versions
         - Replace HttpContext mocks with ASP.NET Core test infrastructure
      
      2. **Integration Tests**: Require significant rework
         - Use WebApplicationFactory for in-memory testing
         - Replace SystemWeb test utilities with Microsoft.AspNetCore.Mvc.Testing
      
      3. **End-to-End Tests**: Often require minimal changes
         - Update URLs if routing changes
         - Adjust authentication flows if mechanisms change
      
      ## Risk Mitigation
      
      1. **Feature Parity Checklist**: Document all legacy features before migration
      2. **Parallel Running**: Run both stacks in production during migration
      3. **Rollback Plan**: Maintain ability to route back to legacy
      4. **Monitoring**: Implement equivalent logging and metrics in both stacks
      5. **Performance Baseline**: Capture legacy performance metrics for comparison
      
      ## Common Pitfalls
      
      1. **Assuming API compatibility**: ASP.NET Core is not a drop-in replacement
      2. **Ignoring IIS dependencies**: Classic pipeline behaviors do not exist in Core
      3. **Session affinity assumptions**: Default session is not sticky in Core
      4. **ViewState replacement**: There is no automatic state preservation
      5. **Global.asax lifecycle**: Application events work differently
      6. **HttpContext.Current**: Static context access does not exist in Core
      
    • patterns.md 12.6 KB
      # Maintenance Patterns for Legacy ASP.NET Code
      
      This document provides patterns for maintaining and stabilizing classic ASP.NET applications on .NET Framework.
      
      ## Stabilization Patterns
      
      ### Isolate Business Logic
      
      Extract business logic from code-behind and controllers into standalone classes:
      
      ```csharp
      // Before: Logic embedded in code-behind
      public partial class OrderPage : Page
      {
          protected void SubmitOrder_Click(object sender, EventArgs e)
          {
              var order = new Order();
              order.CustomerId = int.Parse(CustomerIdField.Text);
              order.Total = CalculateTotal();
              // validation, persistence, notification all inline
          }
      }
      
      // After: Logic in a service class
      public class OrderService
      {
          public OrderResult SubmitOrder(OrderRequest request)
          {
              // validation, persistence, notification encapsulated
          }
      }
      ```
      
      Benefits:
      - Testable without Web Forms infrastructure
      - Portable to other hosting models
      - Clear boundaries for future migration
      
      ### Introduce Dependency Injection Gradually
      
      Add a DI container without requiring a full rewrite:
      
      1. Install a container compatible with System.Web (Autofac, Unity, Simple Injector)
      2. Configure the container in Global.asax Application_Start
      3. Use property injection for Web Forms pages
      4. Use constructor injection for new service classes
      
      ```csharp
      // Global.asax.cs
      protected void Application_Start()
      {
          var builder = new ContainerBuilder();
          builder.RegisterType<OrderService>().As<IOrderService>();
          builder.RegisterType<OrderRepository>().As<IOrderRepository>();
          var container = builder.Build();
      
          // Web Forms property injection
          var propertyInjection = new AutofacWebFormsPropertyInjection(container);
          propertyInjection.InjectDependenciesIntoPage(this);
      }
      ```
      
      ### Wrap Static Dependencies
      
      Isolate HttpContext.Current and other static dependencies:
      
      ```csharp
      // Wrapper interface
      public interface IHttpContextAccessor
      {
          HttpContextBase Current { get; }
      }
      
      // Production implementation
      public class WebHttpContextAccessor : IHttpContextAccessor
      {
          public HttpContextBase Current => new HttpContextWrapper(HttpContext.Current);
      }
      
      // Test implementation
      public class FakeHttpContextAccessor : IHttpContextAccessor
      {
          public HttpContextBase Current { get; set; }
      }
      ```
      
      ### Configuration Abstraction
      
      Wrap ConfigurationManager to enable testing and future migration:
      
      ```csharp
      public interface IAppConfiguration
      {
          string GetConnectionString(string name);
          string GetSetting(string key);
          T GetSection<T>(string sectionName) where T : class;
      }
      
      public class WebConfigConfiguration : IAppConfiguration
      {
          public string GetConnectionString(string name) =>
              ConfigurationManager.ConnectionStrings[name]?.ConnectionString;
      
          public string GetSetting(string key) =>
              ConfigurationManager.AppSettings[key];
      
          public T GetSection<T>(string sectionName) where T : class =>
              ConfigurationManager.GetSection(sectionName) as T;
      }
      ```
      
      ## Web Forms Patterns
      
      ### Reduce ViewState Dependency
      
      Minimize ViewState usage to improve performance and simplify migration:
      
      1. Disable ViewState at page level when not needed: `EnableViewState="false"`
      2. Use explicit hidden fields for required state
      3. Store complex state in session or database
      4. Prefer server-side data retrieval over ViewState round-trips
      
      ### Master Page Consolidation
      
      Reduce layout duplication before migration:
      
      1. Consolidate to a single master page hierarchy
      2. Extract common scripts and styles to a shared location
      3. Use ContentPlaceHolders consistently
      4. Document the master page contract for Razor layout conversion
      
      ### User Control Inventory
      
      Catalog user controls for component migration planning:
      
      | Control | Dependencies | State | Migration Target |
      |---------|--------------|-------|------------------|
      | HeaderControl.ascx | Session, Auth | Minimal | Razor partial |
      | OrderGrid.ascx | ViewState, DataSource | Heavy | Blazor component |
      | SearchBox.ascx | None | None | Tag Helper |
      
      ## MVC Patterns
      
      ### Area Organization
      
      Organize large MVC applications into areas for incremental migration:
      
      ```
      /Areas
        /Orders
          /Controllers
          /Views
          /Models
        /Customers
          /Controllers
          /Views
          /Models
        /Legacy
          /Controllers (unmigrated controllers)
          /Views
      ```
      
      Each area can be migrated independently.
      
      ### Filter Standardization
      
      Consolidate action filters before migration:
      
      ```csharp
      // Standardized exception filter
      public class StandardExceptionFilter : IExceptionFilter
      {
          private readonly ILogger _logger;
      
          public StandardExceptionFilter(ILogger logger)
          {
              _logger = logger;
          }
      
          public void OnException(ExceptionContext filterContext)
          {
              _logger.Error(filterContext.Exception, "Unhandled exception in {Controller}/{Action}",
                  filterContext.RouteData.Values["controller"],
                  filterContext.RouteData.Values["action"]);
      
              filterContext.Result = new ViewResult { ViewName = "Error" };
              filterContext.ExceptionHandled = true;
          }
      }
      ```
      
      ### Route Consolidation
      
      Document and simplify routing before migration:
      
      ```csharp
      // Before: Scattered route definitions
      routes.MapRoute("OrderDetails", "orders/{id}", new { controller = "Orders", action = "Details" });
      routes.MapRoute("OrderList", "orders", new { controller = "Orders", action = "Index" });
      routes.MapRoute("CustomerOrders", "customers/{customerId}/orders", new { controller = "Orders", action = "ByCustomer" });
      
      // After: Consistent attribute routing
      [RoutePrefix("orders")]
      public class OrdersController : Controller
      {
          [Route("")]
          public ActionResult Index() { }
      
          [Route("{id:int}")]
          public ActionResult Details(int id) { }
      
          [Route("~/customers/{customerId:int}/orders")]
          public ActionResult ByCustomer(int customerId) { }
      }
      ```
      
      ## Session and Caching Patterns
      
      ### Session Abstraction
      
      Wrap session access for testing and migration:
      
      ```csharp
      public interface ISessionStore
      {
          T Get<T>(string key);
          void Set<T>(string key, T value);
          void Remove(string key);
      }
      
      public class AspNetSessionStore : ISessionStore
      {
          private readonly HttpSessionStateBase _session;
      
          public AspNetSessionStore(HttpSessionStateBase session)
          {
              _session = session;
          }
      
          public T Get<T>(string key) => (T)_session[key];
          public void Set<T>(string key, T value) => _session[key] = value;
          public void Remove(string key) => _session.Remove(key);
      }
      ```
      
      ### Cache Abstraction
      
      Wrap System.Web.Caching for migration:
      
      ```csharp
      public interface ICacheStore
      {
          T Get<T>(string key);
          void Set<T>(string key, T value, TimeSpan expiration);
          void Remove(string key);
      }
      
      public class WebCacheStore : ICacheStore
      {
          public T Get<T>(string key) => (T)HttpRuntime.Cache.Get(key);
      
          public void Set<T>(string key, T value, TimeSpan expiration)
          {
              HttpRuntime.Cache.Insert(key, value, null,
                  DateTime.UtcNow.Add(expiration),
                  Cache.NoSlidingExpiration);
          }
      
          public void Remove(string key) => HttpRuntime.Cache.Remove(key);
      }
      ```
      
      ## Authentication Patterns
      
      ### Authentication Abstraction
      
      Wrap Forms Authentication for flexibility:
      
      ```csharp
      public interface IAuthenticationService
      {
          void SignIn(string username, bool persistent);
          void SignOut();
          string GetCurrentUser();
          bool IsAuthenticated { get; }
      }
      
      public class FormsAuthenticationService : IAuthenticationService
      {
          public void SignIn(string username, bool persistent)
          {
              FormsAuthentication.SetAuthCookie(username, persistent);
          }
      
          public void SignOut()
          {
              FormsAuthentication.SignOut();
          }
      
          public string GetCurrentUser() => HttpContext.Current.User?.Identity?.Name;
      
          public bool IsAuthenticated => HttpContext.Current.User?.Identity?.IsAuthenticated ?? false;
      }
      ```
      
      ### Role-Based Authorization Consolidation
      
      Standardize role checks before migration:
      
      ```csharp
      public interface IAuthorizationService
      {
          bool HasPermission(string permission);
          bool IsInRole(string role);
          IEnumerable<string> GetRoles();
      }
      
      public class WebAuthorizationService : IAuthorizationService
      {
          public bool HasPermission(string permission)
          {
              // Map permissions to roles or custom logic
              return Roles.IsUserInRole(permission);
          }
      
          public bool IsInRole(string role) => Roles.IsUserInRole(role);
      
          public IEnumerable<string> GetRoles() => Roles.GetRolesForUser();
      }
      ```
      
      ## Logging and Monitoring Patterns
      
      ### Structured Logging Introduction
      
      Add structured logging without changing existing code:
      
      ```csharp
      // Logging abstraction
      public interface IAppLogger
      {
          void Info(string message, params object[] args);
          void Warn(string message, params object[] args);
          void Error(Exception ex, string message, params object[] args);
      }
      
      // Implementation with structured logging
      public class SerilogAppLogger : IAppLogger
      {
          private readonly ILogger _logger;
      
          public SerilogAppLogger(ILogger logger)
          {
              _logger = logger;
          }
      
          public void Info(string message, params object[] args) =>
              _logger.Information(message, args);
      
          public void Warn(string message, params object[] args) =>
              _logger.Warning(message, args);
      
          public void Error(Exception ex, string message, params object[] args) =>
              _logger.Error(ex, message, args);
      }
      ```
      
      ### Health Check Endpoint
      
      Add a health check endpoint for monitoring:
      
      ```csharp
      // HealthController.cs
      public class HealthController : Controller
      {
          private readonly IHealthCheckService _healthCheck;
      
          public HealthController(IHealthCheckService healthCheck)
          {
              _healthCheck = healthCheck;
          }
      
          [Route("health")]
          public ActionResult Index()
          {
              var result = _healthCheck.Check();
              Response.StatusCode = result.IsHealthy ? 200 : 503;
              return Json(result, JsonRequestBehavior.AllowGet);
          }
      }
      ```
      
      ## Testing Patterns
      
      ### Test Seams for Legacy Code
      
      Introduce test seams without major refactoring:
      
      1. **Extract and Override**: Make methods virtual and override in tests
      2. **Subclass and Override**: Create testable subclasses
      3. **Wrap Static Calls**: Create instance wrappers around static dependencies
      
      ```csharp
      // Extract and Override pattern
      public class OrderProcessor
      {
          public virtual DateTime GetCurrentTime() => DateTime.UtcNow;
      
          public bool IsOrderExpired(Order order)
          {
              return order.ExpirationDate < GetCurrentTime();
          }
      }
      
      // Test subclass
      public class TestableOrderProcessor : OrderProcessor
      {
          public DateTime CurrentTime { get; set; }
          public override DateTime GetCurrentTime() => CurrentTime;
      }
      ```
      
      ### Integration Test Infrastructure
      
      Create test infrastructure that mirrors production:
      
      ```csharp
      [TestFixture]
      public class OrderControllerTests
      {
          private TestServer _server;
      
          [SetUp]
          public void Setup()
          {
              _server = new TestServer(new WebHostBuilder()
                  .UseStartup<TestStartup>());
          }
      
          [Test]
          public async Task GetOrders_ReturnsOrders()
          {
              var client = _server.CreateClient();
              var response = await client.GetAsync("/api/orders");
              Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
          }
      }
      ```
      
      ## Deployment Patterns
      
      ### Configuration Transform Management
      
      Organize configuration transforms for maintainability:
      
      ```
      /Web.config
      /Web.Debug.config
      /Web.Release.config
      /Web.Staging.config
      /Web.Production.config
      ```
      
      Use consistent transform patterns:
      
      ```xml
      <!-- Web.Production.config -->
      <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
        <connectionStrings>
          <add name="DefaultConnection"
               connectionString="#{DatabaseConnectionString}#"
               xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
        </connectionStrings>
        <appSettings>
          <add key="Environment" value="Production"
               xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
        </appSettings>
      </configuration>
      ```
      
      ### IIS Configuration as Code
      
      Document IIS configuration for reproducibility:
      
      ```powershell
      # Create application pool
      New-WebAppPool -Name "LegacyAppPool"
      Set-ItemProperty "IIS:\AppPools\LegacyAppPool" -Name "managedRuntimeVersion" -Value "v4.0"
      Set-ItemProperty "IIS:\AppPools\LegacyAppPool" -Name "enable32BitAppOnWin64" -Value $false
      
      # Create website
      New-Website -Name "LegacyApp" -Port 80 -PhysicalPath "C:\inetpub\wwwroot\LegacyApp" -ApplicationPool "LegacyAppPool"
      
      # Configure authentication
      Set-WebConfigurationProperty -Filter "/system.webServer/security/authentication/anonymousAuthentication" -Name "enabled" -Value $true -PSPath "IIS:\Sites\LegacyApp"
      Set-WebConfigurationProperty -Filter "/system.webServer/security/authentication/windowsAuthentication" -Name "enabled" -Value $false -PSPath "IIS:\Sites\LegacyApp"
      ```
      
  • SKILL.md 2 KB
    ---
    name: dotnet-legacy-aspnet
    version: "1.0.0"
    category: "Legacy"
    description: "Maintain classic ASP.NET applications on .NET Framework, including Web Forms, older MVC, and legacy hosting patterns, while planning realistic modernization boundaries."
    compatibility: "Requires classic ASP.NET on .NET Framework."
    ---
    
    # Legacy ASP.NET
    
    ## Trigger On
    
    - working in Web Forms, legacy MVC, or classic ASP.NET applications
    - reviewing old IIS-centric configuration and lifecycle behavior
    - planning migration toward ASP.NET Core without breaking core business flows
    
    ## Workflow
    
    1. Treat classic ASP.NET as a distinct stack with different hosting, lifecycle, and configuration rules from ASP.NET Core.
    2. Stabilize behavior first: routing, session, auth, server controls, configuration transforms, and deployment assumptions.
    3. Plan modernization in seams: isolate domain and service logic, then move replaceable edges instead of rewriting the whole app at once.
    4. Use `dotnet-wcf` or `dotnet-entity-framework6` when the legacy app depends on those subsystems rather than flattening them into generic web work.
    5. Be careful with guidance copied from ASP.NET Core because middleware, DI, and hosting assumptions do not transfer directly.
    6. Validate in an environment that resembles real IIS and configuration transforms.
    
    ## Deliver
    
    - practical maintenance guidance for classic ASP.NET
    - stabilized legacy behavior and modernization seams
    - a migration path that avoids unnecessary risk
    
    ## Validate
    
    - classic and Core guidance are not mixed
    - legacy runtime assumptions are preserved deliberately
    - migration steps are incremental and testable
    
    ## References
    
    - [Migration Paths](references/migration.md): strategies for migrating from ASP.NET to ASP.NET Core, including incremental migration, strangler fig pattern, and component-specific guidance
    - [Maintenance Patterns](references/patterns.md): stabilization and maintenance patterns for legacy ASP.NET code, including abstraction layers, testing seams, and deployment practices
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related