Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/aws-samples/legacy-cycle-store-mvc-app/llms.txt

Use this file to discover all available pages before exploring further.

ASP.NET Core is a complete rewrite of the original ASP.NET stack — not an incremental update. It shares the Model-View-Controller pattern, Razor syntax, and C# controllers with its predecessor, but the underlying hosting model, request pipeline, configuration system, and dependency injection container are entirely new. For the Cycle Store, this means the MVC concepts in place today translate directly to ASP.NET Core equivalents, but the infrastructure code surrounding them must be replaced file by file.

Project File Changes

The legacy project uses a packages.config file to declare NuGet dependencies and a traditional .csproj format that lists every source file explicitly. ASP.NET Core projects use the SDK-style .csproj format, which automatically includes all .cs and .cshtml files in the project directory and declares NuGet packages inline as <PackageReference> items.
<!-- packages.config -->
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.AspNet.Mvc" version="4.0.20710.0" targetFramework="net45" />
  <package id="EntityFramework" version="5.0.0" targetFramework="net45" />
  <package id="Bootstrap" version="3.0.0" targetFramework="net45" />
  <package id="jQuery" version="1.10.2" targetFramework="net45" />
</packages>
The <TargetFramework>netcoreapp3.1</TargetFramework> element replaces the old <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>. The Microsoft.NET.Sdk.Web SDK brings in the ASP.NET Core framework reference automatically.

Startup and Hosting

The legacy application uses Global.asax as its entry point and delegates configuration to static classes (RouteConfig, FilterConfig, BundleConfig). ASP.NET Core replaces this pattern with two files:
  • Program.cs — creates and runs the web host.
  • Startup.cs — configures services (dependency injection) and the HTTP request pipeline (middleware).
// Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace AdventureWorksMVC
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}
// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AdventureWorks.Data;
using AdventureWorks.Business;
using Microsoft.EntityFrameworkCore;

namespace AdventureWorksMVC
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<CycleStoreContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("CycleStore")));

            services.AddScoped<CategoryManager>();
            services.AddScoped<ProductManager>();

            services.AddControllersWithViews();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();
            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
The Global.asax file and all static configuration classes (RouteConfig.cs, FilterConfig.cs, BundleConfig.cs) are deleted from the project.

Controllers

ASP.NET MVC 4 controllers inherit from System.Web.Mvc.Controller. ASP.NET Core controllers inherit from Microsoft.AspNetCore.Mvc.Controller. The method signatures, ActionResult return types, ViewBag, and View() / RedirectToAction() helpers all work the same way.
using System.Web.Mvc;
using AdventureWorks.Business;

namespace AdventureWorksMVC.Controllers
{
    public class HomeController : Controller
    {
        private readonly CategoryManager _categoryManager;

        public HomeController()
        {
            // Direct instantiation — no DI container in MVC 4 by default
            _categoryManager = new CategoryManager();
        }

        public ActionResult Index()
        {
            ViewBag.Categories = _categoryManager.GetMainCategories();
            return View();
        }
    }
}
The using statement is the only mandatory change for most controllers. Constructor injection replaces direct instantiation, and ViewBag, ActionResult, View(), and RedirectToAction() remain fully compatible.

SiteLayoutController Child Actions

The legacy application uses a SiteLayoutController to render shared layout sections — header, footer, and content navigation — via [ChildActionOnly] actions called from the master layout with @{Html.RenderAction("HeaderLayout", "SiteLayout")}. Child actions ([ChildActionOnly]) do not exist in ASP.NET Core MVC. The equivalent feature is View Components — self-contained units that have their own class and partial view, and are invoked from Razor with @await Component.InvokeAsync("ComponentName").
// Modernized: HeaderLayoutViewComponent replaces SiteLayoutController.HeaderLayout()
using Microsoft.AspNetCore.Mvc;
using AdventureWorks.Business;
using System.Threading.Tasks;

namespace AdventureWorksMVC.ViewComponents
{
    public class HeaderLayoutViewComponent : ViewComponent
    {
        private readonly CategoryManager _categoryManager;

        public HeaderLayoutViewComponent(CategoryManager categoryManager)
        {
            _categoryManager = categoryManager;
        }

        public async Task<IViewComponentResult> InvokeAsync()
        {
            var categories = _categoryManager.GetMainCategories();
            return View(categories);
        }
    }
}
The corresponding partial view lives at Views/Shared/Components/HeaderLayout/Default.cshtml. Replace each @{Html.RenderAction(...)} call in _Layout.cshtml with the View Component invocation:
@* Before (MVC 4) *@
@{Html.RenderAction("HeaderLayout", "SiteLayout");}

@* After (ASP.NET Core) *@
@await Component.InvokeAsync("HeaderLayout")

Routing

Convention-based routing is configured in Startup.Configure rather than in a static RouteConfig class. The route pattern syntax is unchanged — {controller}/{action}/{id?} works exactly as before:
// Legacy RouteConfig.cs (deleted in the modernized app)
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}
// Modernized — in Startup.Configure
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});
The {resource}.axd/{*pathInfo} ignore route is not needed in ASP.NET Core because .axd HTTP handler endpoints no longer exist.

Configuration

Web.config is replaced by appsettings.json as the primary configuration file. Environment-specific values are supplied through appsettings.{Environment}.json files or environment variables, which slot naturally into container deployments.
{
  "ConnectionStrings": {
    "CycleStore": "Server=<rds-endpoint>;Database=CYCLE_STORE;User Id=DBUser;Password=<password>;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  }
}
Values previously read from <appSettings> in Web.config via ConfigurationManager.AppSettings["key"] are now read through IConfiguration:
// Legacy
var value = System.Configuration.ConfigurationManager.AppSettings["MySetting"];

// Modernized — IConfiguration injected into Startup constructor
var value = Configuration["MySetting"];
The Web.Debug.config and Web.Release.config XML transform files are removed. In ASP.NET Core, per-environment overrides are handled by appsettings.Development.json and appsettings.Production.json — these files are automatically merged over the base appsettings.json based on the ASPNETCORE_ENVIRONMENT environment variable. For sensitive values (passwords, connection strings) in production, use environment variables or AWS Secrets Manager rather than committing them to any config file.

Views

Razor views (.cshtml) are largely compatible between MVC 4 and ASP.NET Core MVC. Most views migrate without changes. There are two specific patterns to update: 1. Child action calls — replace @{Html.RenderAction(...)} with View Component invocations as described in the SiteLayoutController section above. 2. Layout file naming — the _SiteLayout.cshtml master layout used in the legacy app should be renamed to _Layout.cshtml, which is the ASP.NET Core convention. Update _ViewStart.cshtml to reference the new name:
@* Views/_ViewStart.cshtml *@
@{
    Layout = "_Layout";
}
A _ViewImports.cshtml file should also be added to the Views folder to make tag helpers and common namespaces available across all views:
@* Views/_ViewImports.cshtml *@
@using AdventureWorksMVC
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Error Handling

The legacy application registers a global HandleErrorAttribute filter in FilterConfig.cs to display a generic error view when an unhandled exception occurs. In ASP.NET Core, exception handling is a middleware concern configured in Startup.Configure:
// Legacy FilterConfig.cs (deleted)
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}
// Modernized — in Startup.Configure
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
UseDeveloperExceptionPage() shows the full stack trace during local development. UseExceptionHandler("/Error") redirects to an error controller action in production — add an ErrorController with an Index action and a corresponding view to handle this gracefully.

Build docs developers (and LLMs) love