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.

The Legacy Cycle Store follows a classic three-tier web application architecture: an ASP.NET MVC 4 presentation layer handles HTTP requests and renders Razor views, a hand-written business layer in the AdventureWorks.Business namespace encapsulates data access logic through Entity Framework 5, and a SQL Server database hosted on Amazon RDS provides durable storage. All three tiers live within a single Visual Studio project, AdventureWorksMVC, contained in the AdventureWorksMVC_2013.sln solution. The AWS infrastructure — RDS instance, Secrets Manager, IAM Role, and Security Group — is declared in a CloudFormation template that ships alongside the source code, making the full environment reproducible from a single stack deployment.

Solution Structure

The solution file AdventureWorksMVC_2013.sln contains a single C# project targeting .NET Framework 4.5. The project directory is organized into the conventional ASP.NET MVC layout, with a Business/ subfolder added to co-locate EF data-access concerns alongside the MVC project rather than splitting them into a separate class library.
AdventureWorksMVC_2013.sln
└── AdventureWorksMVC/
    ├── App_Start/
    │   ├── FilterConfig.cs         # Global MVC filter registration
    │   ├── RouteConfig.cs          # URL route table
    │   └── WebApiConfig.cs         # Web API configuration (not used in UI flow)
    ├── Business/
    │   ├── CycleModel.edmx         # EDMX Database-First model diagram
    │   ├── CycleModel.Context.cs   # CYCLE_STOREEntities DbContext (T4-generated)
    │   ├── CycleModel.cs           # T4-generated entity partial classes
    │   ├── Product.cs              # Product entity
    │   ├── ProductCategory.cs      # ProductCategory entity
    │   ├── ProductSubcategory.cs   # ProductSubcategory entity
    │   ├── CategoryManager.cs      # Category query methods
    │   ├── ProductManager.cs       # Product query methods
    │   └── Common.cs               # Shared DataEntities accessor
    ├── Controllers/
    │   ├── HomeController.cs       # Landing page
    │   ├── SiteLayoutController.cs # Layout partials (header, footer, content)
    │   └── ErrorController.cs      # Error handling
    ├── Models/
    │   └── SiteLayoutModel.cs      # View model for layout partials
    ├── Views/
    │   ├── Home/Default.cshtml     # Main landing view
    │   ├── SiteLayout/
    │   │   ├── HeaderLayout.cshtml
    │   │   └── ContentLayout.cshtml
    │   ├── Error/Default.cshtml
    │   └── Shared/_SiteLayout.cshtml  # Master layout
    ├── Helpers/
    │   └── WebResource.cs
    ├── Global.asax / Global.asax.cs
    ├── Web.config
    └── packages.config
App_Start/ follows the convention introduced in MVC 4: startup registrations (RouteConfig.RegisterRoutes, FilterConfig.RegisterGlobalFilters, WebApiConfig.Register) are called from Global.asax.cs in Application_Start, keeping bootstrapping code modular and out of the HttpApplication subclass.

Presentation Layer

The presentation layer is built on ASP.NET MVC 4 (Microsoft.AspNet.Mvc 4.0.20710.0) with Razor 2 views. Page rendering is decomposed across two controllers: HomeController manages the application entry point, and SiteLayoutController renders the shared chrome as child actions called directly from the master layout.

HomeController

HomeController exposes a single Default action that sets a body CSS class on ViewBag and returns the Default.cshtml view. This is the target of the default route (Home/Default).
namespace AdventureWorks.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Default()
        {
            ViewBag.BodyClass = "homepage";
            return View();
        }
    }
}

SiteLayoutController

SiteLayoutController provides three partial-view actions consumed by _SiteLayout.cshtml via Html.RenderAction. FooterLayout and ContentLayout both call CategoryManager.GetMainCategories() to populate the navigation category list before returning their view models.
namespace AdventureWorks.Controllers
{
    public class SiteLayoutController : Controller
    {
        public ActionResult HeaderLayout()
        {
            SiteLayoutModel Header = new SiteLayoutModel();
            return View(Header);
        }

        public ActionResult FooterLayout()
        {
            SiteLayoutModel Footer = new SiteLayoutModel();
            Footer.ProductCategories = CategoryManager.GetMainCategories();
            return View(Footer);
        }

        public ActionResult ContentLayout()
        {
            SiteLayoutModel Header = new SiteLayoutModel();
            Header.ProductCategories = CategoryManager.GetMainCategories();
            return View(Header);
        }
    }
}

Master Layout

Views/Shared/_SiteLayout.cshtml is the Razor master layout. It renders the HeaderLayout child action inline in the <body> tag, wraps @RenderBody() in a YUI grid container, and applies the @ViewBag.BodyClass CSS class set by individual controllers.
<body class='@ViewBag.BodyClass' id="body1">
    @{Html.RenderAction("HeaderLayout", "SiteLayout");}
    <div id="doc2" class="yui-t3 wrapper">
        <div id="bd">
            <div id="yui-main">
                <div class="content">
                    <div>
                        @RenderBody()
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
The SiteLayoutModel view model carries the List<ProductCategory> used to build navigation menus in ContentLayout and FooterLayout:
namespace AdventureWorks.Models
{
    public class SiteLayoutModel
    {
        public string AnonymousTemplateVisibility { get; set; }
        public string LoggedInTemplateVisibility { get; set; }
        public string LoggedInEmailID { get; set; }
        public string ShoppingCartItemsCount { get; set; }
        public List<ProductCategory> ProductCategories { get; set; }
    }
}

Business Layer

The AdventureWorks.Business namespace is the application’s data-access and domain layer. It contains three types of components: the EF-generated DbContext, the T4-generated entity classes, and hand-written manager classes that expose LINQ queries as static methods.

CYCLE_STOREEntities DbContext

CYCLE_STOREEntities is a DbContext subclass generated by the T4 context template (CycleModel.Context.tt). It connects to the database via the CYCLE_STOREEntities named connection string in Web.config and exposes three DbSet properties — one per mapped table.
namespace AdventureWorks.Business
{
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;

    public partial class CYCLE_STOREEntities : DbContext
    {
        public CYCLE_STOREEntities()
            : base("name=CYCLE_STOREEntities")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

        public DbSet<Product> Products { get; set; }
        public DbSet<ProductCategory> ProductCategories { get; set; }
        public DbSet<ProductSubcategory> ProductSubcategories { get; set; }
    }
}
The OnModelCreating override explicitly throws UnintentionalCodeFirstException — a deliberate guard that enforces Database-First mode. If any code-first conventions were to silently build a model from entity classes instead of reading it from the EDMX, EF 5 raises this exception at runtime.

Common — Shared DbContext Accessor

Common is a static helper that provides a shared CYCLE_STOREEntities instance to all manager classes through the DataEntities property. Both CategoryManager and ProductManager reference Common.DataEntities to obtain a context for each query.
namespace AdventureWorks.Business
{
    public class Common
    {
        public static CYCLE_STOREEntities DataEntities
        {
            get
            {
                return new CYCLE_STOREEntities();
            }
        }
    }
}

CategoryManager

CategoryManager provides static LINQ-based query methods over ProductCategory and ProductSubcategory. GetMainCategories() is called on every page load by SiteLayoutController to populate navigation menus.
namespace AdventureWorks.Business
{
    public class CategoryManager
    {
        public static List<ProductCategory> GetMainCategories()
        {
            var cats = from cat in Common.DataEntities.ProductCategories
                       select cat;
            return cats.ToList();
        }

        public static ProductCategory GetCategoryByName(string name)
        {
            var cats = from cat in Common.DataEntities.ProductCategories
                       where cat.Name == name
                       select cat;
            return cats.FirstOrDefault();
        }

        public static ProductSubcategory GetProductSubcategoryByName(string name)
        {
            var cats = from cat in Common.DataEntities.ProductSubcategories
                       where cat.Name == name
                       select cat;
            return cats.FirstOrDefault();
        }
    }
}

ProductManager

ProductManager provides product lookup methods including search by name, subcategory, product ID, color, size, and weight. These queries will require migration from DbSet<Product> to EF Core equivalents in Part 2 of the series.

Data Layer

The CYCLE_STORE SQL Server database stores all product data under the Production schema. The schema is created by CYCLE_STORE_Schema_data.sql, which ships with the repository and must be executed against the RDS instance after provisioning.

Production Schema Tables

Three tables model the product catalog. They mirror a subset of the canonical AdventureWorks schema:
-- Production.ProductCategory
CREATE TABLE [Production].[ProductCategory] (
    [ProductCategoryID] [int]              NOT NULL,
    [Name]              [nvarchar](50)     NOT NULL,
    [rowguid]           [uniqueidentifier] NOT NULL,
    [ModifiedDate]      [datetime]         NOT NULL,
    CONSTRAINT [PK_ProductCategory] PRIMARY KEY CLUSTERED ([ProductCategoryID] ASC)
);

-- Production.ProductSubcategory
CREATE TABLE [Production].[ProductSubcategory] (
    [ProductSubcategoryID] [int]              NOT NULL,
    [ProductCategoryID]    [int]              NOT NULL,
    [Name]                 [nvarchar](50)     NOT NULL,
    [rowguid]              [uniqueidentifier] NOT NULL,
    [ModifiedDate]         [datetime]         NOT NULL,
    CONSTRAINT [PK_ProductSubcategory] PRIMARY KEY CLUSTERED ([ProductSubcategoryID] ASC)
);

-- Production.Product
CREATE TABLE [Production].[Product] (
    [ProductID]            [int]           NOT NULL,
    [Name]                 [nvarchar](50)  NOT NULL,
    [ProductNumber]        [nvarchar](25)  NOT NULL,
    [Color]                [nvarchar](15)  NULL,
    [StandardCost]         [money]         NOT NULL,
    [ListPrice]            [money]         NOT NULL,
    [Size]                 [nvarchar](5)   NULL,
    [Weight]               [decimal](8,2)  NULL,
    [ProductSubcategoryID] [int]           NULL,
    [SellStartDate]        [datetime]      NOT NULL,
    -- ... additional columns
    CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED ([ProductID] ASC)
);
The entity classes in AdventureWorks.Business are generated directly from this schema by the EDMX T4 templates. ProductCategory maintains a navigation property ICollection<ProductSubcategory>, and ProductSubcategory holds a back-reference virtual ProductCategory ProductCategory. These navigation properties are what EF 5 lazy-loads — and what EF Core requires explicit .Include() calls to replace.

AWS Infrastructure

The SqlServerRDSFixedUidPwd.yaml CloudFormation template provisions all AWS resources required to run the application. Deploying the stack creates five resources in the target account and region:
The RDS instance is configured with PubliclyAccessible: true and a Security Group that opens TCP port 1433 to 0.0.0.0/0. This configuration is intended for development and demo use only. In any environment handling real data, restrict the Security Group ingress to specific CIDR ranges or VPC security groups, and set PubliclyAccessible to false.

CloudFormation Resources

Resources:
  # 1. Secrets Manager — stores database credentials
  CycleStoreCreds:
    Type: 'AWS::SecretsManager::Secret'
    Properties:
      Name: CycleStoreCredentials
      Description: "This secret has a static user id and password for cycle store db."
      SecretString: '{"username":"DBUser","password":"DBU$er2020"}'
      Tags:
        - Key: AppName
          Value: CycleStore

  # 2. IAM Role — grants RDS permission to use S3 for native backups
  RdsS3FullAccessRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: RDS-Sqlex--S3-FullAccess
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: rds.amazonaws.com
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: AmazonS3FullAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: 's3:*'
                Resource: '*'

  # 3. Security Group — opens port 1433 (dev/demo only)
  SQLServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: SQL Server Security Group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '1433'
          ToPort: '1433'
          CidrIp: 0.0.0.0/0

  # 4. Option Group — enables SQLSERVER_BACKUP_RESTORE via the IAM Role
  OptionGroup:
    Type: AWS::RDS::OptionGroup
    Properties:
      EngineName: sqlserver-ex
      MajorEngineVersion: "14.00"
      OptionConfigurations:
        - OptionName: SQLSERVER_BACKUP_RESTORE
          OptionSettings:
            - Name: IAM_ROLE_ARN
              Value: !GetAtt RdsS3FullAccessRole.Arn

  # 5. RDS DB Instance — SQL Server Express, db.t2.micro, 20 GB
  SQLDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: sqlserver-ex
      EngineVersion: 14.00.3281.6.v1
      DBInstanceClass: db.t2.micro
      AllocatedStorage: '20'
      LicenseModel: license-included
      MultiAZ: false
      PubliclyAccessible: 'true'
      MasterUsername: DBUser
      MasterUserPassword: <change-here>
      OptionGroupName: !Ref OptionGroup
The stack output SQLDatabaseEndpoint returns the full hostname:port string used in the application’s Web.config connection string.

Infrastructure Summary

ResourceTypeKey Configuration
CycleStoreCredsSecrets Manager SecretName: CycleStoreCredentials
RdsS3FullAccessRoleIAM RoleGrants s3:* to rds.amazonaws.com
SQLServerSecurityGroupEC2 Security GroupTCP 1433 open to 0.0.0.0/0
OptionGroupRDS Option GroupSQLSERVER_BACKUP_RESTORE with IAM Role ARN
SQLDatabaseRDS DB Instancesqlserver-ex 14.00, db.t2.micro, 20 GB

MVC Routing

Route registration happens in App_Start/RouteConfig.cs, called from Global.asax.cs during Application_Start. The application uses a single conventional route with the Home controller and Default action as the entry point:
namespace AdventureWorks
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new
                {
                    controller = "Home",
                    action = "Default",
                    id = UrlParameter.Optional
                }
            );
        }
    }
}
Global.asax.cs wires all App_Start registrations during application startup:
public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}
When a request arrives at the application root (/), the route engine matches it to Home/Default/ and dispatches to HomeController.Default(), which sets ViewBag.BodyClass = "homepage" and renders Views/Home/Default.cshtml within the _SiteLayout.cshtml master layout.

Project Structure

Detailed walkthrough of every directory and file in the Visual Studio solution.

Data Model

Full schema reference for the CYCLE_STORE database and the EDMX entity mappings.

Business Layer

Deep dive into CategoryManager, ProductManager, and the Common DbContext accessor pattern.

MVC Layer

Controllers, Razor views, view models, and the SiteLayout child action pattern explained.

Build docs developers (and LLMs) love