Assignment 3
Course Number and Name: COMP 466 - Advanced Technologies for Web-Based Systems
Student Name: Sammy Dods
Student ID: REDACTED
Date Started: Aug 22, 2025
Date Completed: Jan 26, 2026
Estimated Hours Spent: 80
Part 1 - Visitor Tracking Application
Technology Stack: ASP.NET Web Forms, Bootstrap 5, jQuery
Framework: .NET Framework 4.7.2
Data Storage: Browser Cookies
Project Description and Requirements
Part 1 is a simple visitor tracking web application that monitors and displays visitor information. The application tracks the number of times a visitor has accessed the site, displays their IP address, and detects their timezone using client-side JavaScript.
Requirements
- Track and display the number of visits using browser cookies
- Display the visitor's IP address
- Detect and display the visitor's timezone using JavaScript
- Use ASP.NET Web Forms architecture
- Implement Bootstrap styling for modern UI
- Use Master Page for consistent layout
Analysis and Design
Architecture
The application uses ASP.NET Web Forms with a Master Page pattern for consistent layout. The main page (Default.aspx) handles visitor tracking logic in teh code-behind file.
Data Flow
flowchart TD
A[User Visits Page] --> B[Page_Load Event Fires]
B --> C{Cookie Exists?}
C -->|Yes| D[Increment Count]
C -->|No| E[Initialize to 1]
D --> F[Get IP Address from server variables]
E --> F
F --> G[Display Visit Count & IP Address]
G --> H[JavaScript Detects Timezone]
H --> I[Display All Info to User]
- User visits Default.aspx
- Page_Load event checks for "NumVisits" cookie
- If cookie exists, increment visit count; otherwise, initialize to 1
- Retrieve IP address from server variables
- Display visit count and IP address on page
- Client-side JavaScript detects timezone using Intl.DateTimeFormat API
Note: The Page_Load event fires every time the page loads, which is perfect for tracking visits. I had to be careful with the cookie logic - if the cookie doesn't exist, I create a new one, otherwise I increment the existing value. The IP address thing was way trickier than I thought it'd be because of proxies and stuff. Took me a while to figure that out.
Key Components
- Site.master: Master page providing consistent layout and Bootstrap styling
- Default.aspx: Main page displaying visitor information
- Default.aspx.cs: Code-behind handling cookie management and IP adress retrieval
Implementation Documentation
Cookie Management
protected void Page_Load(object sender, EventArgs e)
{
int numVisits = 1;
HttpCookie cookie = Request.Cookies["NumVisits"];
if (cookie != null)
{
numVisits = int.Parse(cookie.Value) + 1;
}
else
{
cookie = new HttpCookie("NumVisits");
}
cookie.Value = numVisits.ToString();
cookie.Expires = DateTime.Now.AddDays(30);
Response.Cookies.Add(cookie);
string ipAddress = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(ipAddress))
{
ipAddress = Request.ServerVariables["REMOTE_ADDR"];
}
litNumVisits.Text = numVisits.ToString();
litIPAddress.Text = ipAddress;
}
Timezone Detection
Timezone is detected client-side using JavaScript's Intl.DateTimeFormat API:
<script>
document.getElementById("timezone").innerText = Intl.DateTimeFormat().resolvedOptions().timeZone;
</script>
Note: I tried to get the timezone server-side at first, but that was a pain. Way easier to just use the browser's built-in API. The Intl.DateTimeFormat thing works great in modern browsers and I don't need any extra libraries, so that's nice.
Project Structure
- Site.master / Site.master.cs: Master page with Bootstrap styling
- Default.aspx / Default.aspx.cs: Main visitor tracking page
- Global.asax / Global.asax.cs: Application lifecycle events
Note: I kept the project structure simple for Part 1. The Master Page pattern is really useful - I used it in all parts to maintain consistent styling. Bootstrap made the UI look professional without too much custom CSS work. I use bootstrap every day at my job, so it was easy to plug in here.
User Guide
Setup Instructions
- Open the solution file (TM3A.sln) in Visual Studio
- Set part1 as the startup project (right-click part1 → Set as StartUp Project)
- Build the solution (Build → Build Solution)
- Run the application (F5 or Debug → Start Debugging)
- The application will open in your browser at Default.aspx
Using the Application
- Upon first visit, the visit count will be 1
- Refresh the page or visit again - the visit count will increment
- The IP address is displayed automatically
- The timezone is detected and displayed automatically
- Visit count persists for 30 days via browser cookie
Note: When I was testing, I noticed that clearing browser cookies resets the visit count, which makes sense. The IP address might show as "::1" when testing locally (that's IPv6 localhost), which is totally normal. The timezone detection works great - it just automatically figures out what timezone the user is in.
Accessing the Application
Note to Instructor: This application demonstrates basic cookie management, server variable access, and client-side JavaScript integration in ASP.NET Web Forms. The visit counter persists accross browser sessions using cookies with a 30-day expiration.
Personal note: This was a good starting point to get familiar with ASP.NET Web Forms. I chose a 30-day expiration for the cookie because it seemed like a reasonable balance - long enough that users would see their visit count persist, but not so long that it would clutter their browser. I also learned that IP address detection can be tricky with proxies and load balancers, which is why I check both HTTP_X_FORWARDED_FOR and REMOTE_ADDR.
Part 2 - Picture Slideshow Application
Technology Stack: ASP.NET Web Forms, Bootstrap 5, jQuery, JavaScript
Framework: .NET Framework 4.7.2
Data Storage: Text file (App_Data/pictures.txt)
Project Description and Requirements
Part 2 is a picture slideshow application that displays images in a slideshow format. The application reads picture information from a server-side text file and provides controls for starting/stopping the slideshow, toggling between random and sequential modes, and manually navigating throught pictures.
Requirements
- Store picture information (name, URL, description) in a text file on the server
- Display pictures in a slideshow format
- Show caption/description under each picture
- Provide Start/Stop button to control the slideshow
- Toggle between random and sequential display modes
- Provide Back and Next buttons for manual navigation (only enabled in sequential mode)
- Allow configuration of display interval (in seconds)
- Display total number of pictures
Analysis and Design
Architecture
The application uses ASP.NET Web Forms with a Master Page. Picture data is loaded from a text file on the server and serialized to JSON for client-side JavaScript processing.
Data Storage Format
Picture information is stored in App_Data/pictures.txt with the following format:
Picture Name|Image URL|Description
Example Picture|https://example.com/image.jpg|This is an example picture
Each line contains three fields separated by pipe (|) characters: Name, URL, and Description.
Note: I went with a simple text file because it's super easy to edit. I thought about using JSON or XML, but honestly a plain text file with '|' as a separator was way simpler. The pipe character works good as a separator since you probably won't see it in picture names or URLs.
Slideshow Modes
- Sequential Mode: Pictures display in order, with manual navigation buttons enabled
- Random Mode: Pictures display in random order, manual navigation disabled
Slideshow Flow
flowchart TD
A[Page Loads] --> B[Load Pictures from Text File]
B --> C[Serialize to JSON Send to Client]
C --> D{User Clicks Start?}
D -->|No| E[Wait for Input]
D -->|Yes| F[Start Slideshow]
F --> G{Random Mode?}
G -->|Yes| H[Shuffle Array
Disable Nav Buttons]
G -->|No| I[Sequential Order
Enable Nav Buttons]
H --> J[Display Picture at Interval]
I --> J
J --> K{User Clicks Stop?}
K -->|No| J
K -->|Yes| L[Stop Timer]
L --> M[Show Current Picture]
E --> D
Implementation Documentation
Picture Loading (Server-Side)
private List<Picture> LoadPictures()
{
var list = new List<Picture>();
var path = Server.MapPath("~/App_Data/pictures.txt");
var lines = System.IO.File.ReadAllLines(path);
foreach (var raw in lines)
{
var line = raw.Trim();
if (string.IsNullOrEmpty(line)) continue;
var parts = line.Split(new[] { '|' }, 3);
if (parts.Length != 3) continue;
var name = parts[0].Trim();
var url = parts[1].Trim();
var desc = parts.Length >= 3 ? parts[2].Trim() : "";
list.Add(new Picture()
{
Name = name,
Url = url,
Description = desc
});
}
return list;
}
Slideshow Control (Client-Side JavaScript)
Key JavaScript functions:
- showAt(i): Displays picture at index i
- startShow(): Starts the slideshow with configured interval
- stopShow(): Stops the slideshow
- stepForward() / stepBackward(): Manual navigation functions
- updateManualButtons(): Enables/disables navigation buttons based on mode
Note: The JavaScript for this was way more complicated than I thought it'd be. I had to be careful with the interval timer - make sure to clear it when stopping, and don't let it create a bunch of timers if someone clicks start a bunch of times. The random mode just uses Math.random() to shuffle stuff around, which works fine for small picture sets.
Project Structure
- Site.master / Site.master.cs: Master page (no navbar, minimal layout)
- Slide.aspx / Slide.aspx.cs: Main slideshow page
- Models/Picture.cs: Picture model class
- App_Data/pictures.txt: Picture data file
- Default.aspx: Redirects to Slide.aspx
User Guide
Setup Instructions
- Open the solution file (TM3A.sln) in Visual Studio
- Set part2 as the startup project
- Ensure
App_Data/pictures.txt exists with picture data in the format: Name|URL|Description
- Build and run the application
- The application will redirect to Slide.aspx automatically
Using the Slideshow
- Click "Start" to begin the slideshow
- Adjust the interval (in seconds) using the number input
- Check "Random Mode" to display pictures randomly
- In sequential mode, use "Back" and "Next" buttons to navigate manually
- Click "Stop" to pause the slideshow
- The caption appears below each picture
- The total picture count is displayed at the bottom
Note: I messed around with different interval values - too fast (under 2 seconds) and you can't really see the pictures, too slow (over 10 seconds) and it gets boring. 3 seconds seems like a sweet spot. The random mode is pretty cool - it never shows the same picture twice in a cycle, which I did by shuffling the array.
Picture Data File Format
Create or edit App_Data/pictures.txt with one picture per line:
Sunset Beach|https://example.com/sunset.jpg|Beautiful sunset over the ocean
Mountain View|https://example.com/mountain.jpg|Snow-capped mountain peak
City Skyline|https://example.com/city.jpg|Urban cityscape at night
Accessing the Application
Note to Instructor: This application demonstrates file I/O operations, JSON serialization, client-side JavaScript state management, and dynamic UI updates. The slideshow supports both automatic and manual navigation modes with proper button state managment.
Personal note: This part was more challenging than Part 1, especially getting the random vs sequential modes to work correctly. I had some issues initially with the buttons not enabling/disabling properly, but eventually figured out the state management. The interval control was a nice touch - I tested it with different values and found that 3-5 seconds works well for most pictures. Also learned that JSON serialization in C# is really straightforward with Newtonsoft.Json.
Part 3 - Computer Store (Basic Version)
Technology Stack: ASP.NET Web Forms, Bootstrap 5, jQuery, JSON
Framework: .NET Framework 4.7.2
Data Storage: Hardcoded data (StaticData.cs), Browser Cookies for cart
Project Description and Requirements
Part 3 is the initial version of the computer store application, created as the foundation before Part 4. It provides a core shopping experience without user authentication or database integration. Computer and component data are hardcoded, and the shopping cart is stored in browser cookies. This version establishes the basic functionality that is later extended in Part 4.
Requirements
- Display available computer systems with default configurations
- Allow customers to customize computer components (CPU, RAM, Storage, GPU, Monitor, Power Supply, Case, OS, Sound Card)
- Calculate total price dynamically based on selected components
- Add customized computers to shopping cart
- View and manage shopping cart
- Proceed to checkout (no order saving, just cart clearing)
- No user registration or login required
- No database - all data hardcoded
- Cart stored in browser cookies as JSON
Analysis and Design
Architecture
The application uses ASP.NET Web Forms with hardcoded data in StaticData.cs. The shopping cart is managed entirely through browser cookies, and no persistent storage is used.
Data Model
- Computer: Base computer with default component configuration
- Component: Individual parts (CPU, RAM, Storage, etc.) with prices
- CartItem: Selected computer with customized components and total price
Key Features
- Dynamic price calculation using AJAX calls to WebMethods
- Real-time component selection and price updates
- JSON serialization for cart storage in cookies
- Bootstrap styling for modern UI
The prices I used are based on market research from when I started this project - for example, the RAM prices were set before the recent DDR5 RAM shortages that drove prices up significantly. If I were to update this now, I'd probably increase the RAM prices by an unreasonable ammount to reflect current market conditions. The prices are based on a more consumer friendly fantasty world (haha)
Shopping Flow
flowchart TD
A[User Visits Products Page] --> B[Display Available Computers]
B --> C{User Clicks Configure & Buy?}
C -->|No| B
C -->|Yes| D[Load Configuration Page
Show Component Dropdowns]
D --> E{User Changes Component?}
E -->|Yes| F[AJAX Call Update Price]
E -->|No| G{User Clicks Add to Cart?}
F --> G
G -->|No| D
G -->|Yes| H[Save to Cookie]
H --> I[View Cart]
I --> J{Proceed to Checkout?}
J -->|No| B
J -->|Yes| K[Clear Cart]
K --> L[Order Complete No DB Save]
Implementation Documentation
Hardcoded Data Structure
All computer and component data is defined in DAL/StaticData.cs:
public static class StaticData
{
public static List<Component> GetAllComponents() { ... }
public static Component GetComponentById(string id) { ... }
public static List<Computer> GetAllComputers() { ... }
public static Computer GetComputerById(string id) { ... }
}
Note: I just threw all the data into static lists in the class. Made it super easy to add or change stuff while I was working on it. I used IDs like "cpu-i7" and "ram-16" so the code makes sense when you read it. The component categories are basically what you'd see at a real computer store.
Cart Management
Cart items are stored in browser cookies as JSON:
var cart = JsonConvert.DeserializeObject<List<CartItem>>(cookie.Value) ?? new List<CartItem>();
cart.Add(newCartItem);
var cookie = new HttpCookie("cart", JsonConvert.SerializeObject(cart));
cookie.Expires = DateTime.Now.AddDays(30);
Response.Cookies.Add(cookie);
Note: I know cookies have size limitations (usually 4KB), but for a typical shopping cart with a few items, this works fine. The JSON serialization makes it easy to store complex objects.
Dynamic Price Calculation
AJAX WebMethod provides real-time price updates:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static string GetComponentPrice(string componentId)
{
var component = StaticData.GetComponentById(componentId);
return component != null ? component.Price.ToString("F2") : "0.00";
}
Note: The AJAX calls make the price updates feel pretty snappy. I had to make sure the WebMethod was static and had the right attributes or it wouldn't work. The "F2" formatting thing makes sure we always show two decimal places, which looks better. If a component ID doesn't exist, it just returns 0.00 as a safe bet.
Project Structure
- Store/Products.aspx: Display available computers
- Store/Configure.aspx: Component customization page
- Store/Cart.aspx: Shopping cart management
- Store/Checkout.aspx: Checkout page (clears cart, no order saving)
- DAL/StaticData.cs: Hardcoded computer and component data
- Models/: Computer, Component, CartItem model classes
User Guide
Setup Instructions
- Open the solution file (TM3A.sln) in Visual Studio
- Set part3 as the startup project
- Build and run the application
Using the Application
- Browse available computers on the Products page
- Click "Configure & Buy" on any computer
- Customize components using the dropdown menus
- Watch the total price update in real-time
- Click "Add to Cart" to add the configured computer
- View cart to see all items and total
- Remove items from cart if needed
- Proceed to checkout (cart will be cleared, no order is saved)
Note: The real-time price updates were probably my favorite thing to build. Makes the whole shopping thing feel way more modern and interactive. I tested it with a bunch of different component combos to make sure the math was right. The cart stays in cookies so users can close the browser and come back later, which is pretty handy.
Accessing the Application
Note to Instructor: This application represents the initial e-commerce implementation created before Part 4. It demonstrates the foundational e-commerce flow without database dependencies. It showcases AJAX-based dynamic updates, JSON cookie management, and component-based pricing calculations. This version was later extended in Part 4 with user authentication and order persistence.
Personal note: This part was pretty fun to build! I spent way too much time getting the dynamic price calculation to work right - working with AJAX was cool though. I had to think about the component categories a bit. I went with 9 categories total, which seemed like a good middle ground between realistic and not too complicated. The "No Monitor" and "No Sound Card" options are there because not everyone needs those, especially if you're building a server or already got external stuff. Building this without a database first was actually a good learning thing - helped me figure out what data I'd need when I moved to Part 4.
Part 4 - Computer Store with Database Integration
Technology Stack: ASP.NET Web Forms, ADO.NET, SQL Server LocalDB, Bootstrap 5, jQuery
Framework: .NET Framework 4.7.2
Database Access: ADO.NET with parameterized queries for security
Project Overview
Part 4 is an extended and advanced version of the computer store application, built upon the foundation established in Part 3. This version adds comprehensive customer management, order management, and database integration using ADO.NET. The system extends Part 3's functionality by adding user authentication, order tracking, and persistent data storage. The application maintains the same ASP.NET Web Forms architecture and folder structure from Part 3, with additional features and database intergration.
Database Design
Database Schema Overview
The database consists of 5 main tables designed to support a complete e-commerce system:
Note: I chose to use 5 tables instead of a more normalized approach with separate junction tables. This keeps the schema simpler and works well for this application's needs. I stored component configurations as JSON strings in the Computers and OrderItems tables, which allows flexibility without needing additional tables. the JSON approach made sense and kept the database structure manageable.
Application Flow
flowchart TD
A[User Visits Site] --> B{Logged In?}
B -->|No| C[Register/Login]
B -->|Yes| D[Browse Products]
C --> D
D --> E{Configure Computer?}
E -->|No| D
E -->|Yes| F[Select Components
Calculate Price]
F --> G[Add to Cart Cookie Storage]
G --> H{Checkout?}
H -->|No| D
H -->|Yes| I[Enter Shipping Info]
I --> J[Create Order in Database]
J --> K[Create Order Items
Save Component Config]
K --> L[Clear Cart]
L --> M[View Order Details]
Registration/Login Flow
flowchart TD
A[User Wants to Register] --> B[Fill Registration Form]
B --> C{Email Already Exists?}
C -->|Yes| D[Show Error]
C -->|No| E[Hash Password]
D --> B
E --> F[Save to Database
Create Session]
F --> G[User Logged In]
Table Structure
| Table Name |
Purpose |
Primary Key |
Key Relationships |
| Customers |
Stores customer information and authentication credentials |
Id (INT, IDENTITY) |
Referenced by Orders table |
| Computers |
Available computer configurations and base information |
ID (NVARCHAR(50)) |
Referenced by OrderItems table |
| Components |
Individual computer components (CPU, RAM, Storage, GPU, etc.) |
ID (NVARCHAR(50)) |
Stored as JSON in Computers and OrderItems |
| Orders |
Customer orders with shipping and payment information |
Id (INT, IDENTITY) |
Foreign Key: CustomerId → Customers(Id) |
| OrderItems |
Individual items within each order |
Id (INT, IDENTITY) |
Foreign Key: OrderId → Orders(Id) ON DELETE CASCADE |
Detailed Table Definitions
Customers Table
| Column Name |
Data Type |
Constraints |
Description |
| Id |
INT |
PRIMARY KEY, IDENTITY(1,1) |
Unique customer identifier |
| FirstName |
NVARCHAR(50) |
NOT NULL |
Customer's first name |
| LastName |
NVARCHAR(50) |
NOT NULL |
Customer's last name |
| Email |
NVARCHAR(100) |
NOT NULL, UNIQUE |
Customer's email address (used for login) |
| Password |
NVARCHAR(100) |
NOT NULL |
SHA256 hashed password |
Note: I made Email UNIQUE because it's used for login, and we don't want people making duplicate accounts. The password field is 100 characters to fit SHA256 hashes (which are 64 hex characters). I thought about adding a salt column, but for this assignment, SHA256 without salt was fine. In a real app, I'd definitely do something more secure.
Computers Table
| Column Name |
Data Type |
Constraints |
Description |
| PhoneNumber |
NVARCHAR(20) |
NULL |
Contact phone number |
| Address |
NVARCHAR(200) |
NULL |
Street address |
| City |
NVARCHAR(50) |
NULL |
City |
| PostalCode |
NVARCHAR(20) |
NULL |
Postal/ZIP code |
| Country |
NVARCHAR(50) |
NULL |
Country |
| CreatedDate |
DATETIME2 |
NOT NULL |
Account creation timestamp |
| LastLoginDate |
DATETIME2 |
NULL |
Last successful login timestamp |
| IsActive |
BIT |
DEFAULT 1 |
Account active status flag |
Computers Table
| Column Name |
Data Type |
Constraints |
Description |
| ID |
NVARCHAR(50) |
PRIMARY KEY |
Unique computer identifier (e.g., "pc-gamer") |
| Name |
NVARCHAR(100) |
NOT NULL |
Computer name (e.g., "Gaming PC") |
| Description |
NVARCHAR(500) |
NULL |
Computer description |
| BasePrice |
DECIMAL(18,2) |
NOT NULL |
Base price of the computer |
| ImageUrl |
NVARCHAR(500) |
NULL |
URL to computer image |
| DefaultComponentIDsJson |
NVARCHAR(1000) |
NULL |
JSON string containing default component configuration |
Components Table
| Column Name |
Data Type |
Constraints |
Description |
| ID |
NVARCHAR(50) |
PRIMARY KEY |
Unique component identifier (e.g., "cpu-i7") |
| Category |
NVARCHAR(50) |
NOT NULL |
Component category (CPU, RAM, Storage, GPU, etc.) |
| Name |
NVARCHAR(100) |
NOT NULL |
Component name |
| Price |
DECIMAL(18,2) |
NOT NULL |
Component price |
| Description |
NVARCHAR(500) |
NULL |
Component description |
| IsActive |
BIT |
DEFAULT 1 |
Component availability flag |
Orders Table
| Column Name |
Data Type |
Constraints |
Description |
| Id |
INT |
PRIMARY KEY, IDENTITY(1,1) |
Unique order identifier |
| CustomerId |
INT |
NOT NULL, FOREIGN KEY |
Reference to Customers.Id |
| OrderDate |
DATETIME2 |
NOT NULL |
Order placement timestamp |
| Status |
NVARCHAR(50) |
NOT NULL |
Order status (Pending, Processing, Shipped, Delivered, Cancelled) |
| TotalAmount |
DECIMAL(18,2) |
NOT NULL |
Total order amount |
| ShippingAddress |
NVARCHAR(200) |
NULL |
Shipping address |
| BillingAddress |
NVARCHAR(200) |
NULL |
Billing address |
| PaymentMethod |
NVARCHAR(100) |
NULL |
Payment method (Credit Card, PayPal, etc.) |
| Notes |
NVARCHAR(500) |
NULL |
Additional order notes |
OrderItems Table
| Column Name |
Data Type |
Constraints |
Description |
| Id |
INT |
PRIMARY KEY, IDENTITY(1,1) |
Unique order item identifier |
| OrderId |
INT |
NOT NULL, FOREIGN KEY |
Reference to Orders.Id (CASCADE DELETE) |
| ComputerId |
NVARCHAR(50) |
NOT NULL |
Computer identifier |
| ComputerName |
NVARCHAR(100) |
NOT NULL |
Computer name at time of order |
| UnitPrice |
DECIMAL(18,2) |
NOT NULL |
Price per unit |
| Quantity |
INT |
NOT NULL |
Quantity ordered |
| TotalPrice |
DECIMAL(18,2) |
NOT NULL |
Total price for this item |
| SelectedComponentsJson |
NVARCHAR(2000) |
NULL |
JSON string containing selected component configuration |
Entity Relationships
- Customer → Orders: One-to-Many (One customer can have many orders)
- Order → OrderItems: One-to-Many with CASCADE DELETE (One order can have many items; deleting an order deletes all it's items)
- Computer → OrderItems: One-to-Many (One computer can appear in many order items)
Note: I used CASCADE DELETE on the OrderItems table so that if an order is deleted, all its items are automatically removed. This prevents orphaned records and keeps the database clean. I debated whether to allow order deletion at all, but decided it was a useful feature for testing and administrative purposes.
SQL Scripts
Database Creation Script
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ComputerStore')
BEGIN
CREATE DATABASE ComputerStore
END
Table Creation Scripts
The following SQL scripts are executed automatically by the DatabaseHelper.CreateTables() method:
Note: I used IF NOT EXISTS checks everywhere so you can run the scripts a bunch of times without breaking anything. This was super helpful when I was messing around with the schema. The sysobjects check is SQL Server specific, but since we're using LocalDB, that's fine. I learned that NVARCHAR is better than VARCHAR for internationalization stuff, even though it takes up more space.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Components' AND xtype='U')
CREATE TABLE Components (
ID NVARCHAR(50) PRIMARY KEY,
Category NVARCHAR(50) NOT NULL,
Name NVARCHAR(100) NOT NULL,
Price DECIMAL(18,2) NOT NULL,
Description NVARCHAR(500),
IsActive BIT DEFAULT 1
);
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Computers' AND xtype='U')
CREATE TABLE Computers (
ID NVARCHAR(50) PRIMARY KEY,
Name NVARCHAR(100) NOT NULL,
Description NVARCHAR(500),
BasePrice DECIMAL(18,2) NOT NULL,
ImageUrl NVARCHAR(500),
DefaultComponentIDsJson NVARCHAR(1000)
);
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Customers' AND xtype='U')
CREATE TABLE Customers (
Id INT IDENTITY(1,1) PRIMARY KEY,
FirstName NVARCHAR(50) NOT NULL,
LastName NVARCHAR(50) NOT NULL,
Email NVARCHAR(100) NOT NULL UNIQUE,
Password NVARCHAR(100) NOT NULL,
PhoneNumber NVARCHAR(20),
Address NVARCHAR(200),
City NVARCHAR(50),
PostalCode NVARCHAR(20),
Country NVARCHAR(50),
CreatedDate DATETIME2 NOT NULL,
LastLoginDate DATETIME2,
IsActive BIT DEFAULT 1
);
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Orders' AND xtype='U')
CREATE TABLE Orders (
Id INT IDENTITY(1,1) PRIMARY KEY,
CustomerId INT NOT NULL,
OrderDate DATETIME2 NOT NULL,
Status NVARCHAR(50) NOT NULL,
TotalAmount DECIMAL(18,2) NOT NULL,
ShippingAddress NVARCHAR(200),
BillingAddress NVARCHAR(200),
PaymentMethod NVARCHAR(100),
Notes NVARCHAR(500),
FOREIGN KEY (CustomerId) REFERENCES Customers(Id)
);
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='OrderItems' AND xtype='U')
CREATE TABLE OrderItems (
Id INT IDENTITY(1,1) PRIMARY KEY,
OrderId INT NOT NULL,
ComputerId NVARCHAR(50) NOT NULL,
ComputerName NVARCHAR(100) NOT NULL,
UnitPrice DECIMAL(18,2) NOT NULL,
Quantity INT NOT NULL,
TotalPrice DECIMAL(18,2) NOT NULL,
SelectedComponentsJson NVARCHAR(2000),
FOREIGN KEY (OrderId) REFERENCES Orders(Id) ON DELETE CASCADE
);
Data Seeding Script
Initial data is populated automatically on first run. The seeding includes:
- 25 Components: RAM (3 types), CPU (3 types), Storage (5 types), Monitor (4 types), GPU (4 types), Sound Card (3 types), Power Supply (3 types), Case (3 types), Operating System (2 types)
- 5 Computers: Gaming PC, Office PC, Workstation PC, Budget PC, Content Creator PC
Note: It was fun to use my PC knowledge (shout out to linus tech tips)to come up with realistic component prices and configurations. The 5 computer types cover different use cases - gaming, office work, professional workstations, budget builds, and content creation. Each has a default configuration that makes sense for that use case. The component selection gives users good options without being overwhelming.
Application Architecture
Technology Stack
- Framework: ASP.NET Web Forms (.NET Framework 4.7.2)
- Database Access: ADO.NET with SQL Server LocalDB
- Frontend: Bootstrap 5, jQuery 3.7.0
- Data Format: JSON for cart storage and component configurations
- Security: SHA256 password hashing, parameterized SQL queries
Project Structure
- Root/ - Default.aspx (Home page)
- Account/ - Login.aspx, Register.aspx, Profile.aspx, Logout.aspx, ForgotPassword.aspx
- Store/ - Products.aspx, Configure.aspx, Cart.aspx, Checkout.aspx
- Orders/ - MyOrders.aspx, OrderDetails.aspx, EditOrder.aspx
- About/ - Contact.aspx, Feedback.aspx
- DAL/ - Data Access Layer (DatabaseHelper, CustomerDataAccess, ComputerDataAccess, ComponentDataAccess, OrderDataAccess)
- Models/ - Entity classes (Customer, Computer, Component, Order, OrderItem, CartItem)
Data Access Layer (DAL)
- DatabaseHelper.cs: Central database connection and query execution class
- GetConnection() - Creates SQL connection using connection string from Web.config
- ExecuteNonQuery() - Executes INSERT, UPDATE, DELETE queries
- ExecuteScalar() - Executes queries returning single value (e.g., SCOPE_IDENTITY())
- ExecuteDataTable() - Executes SELECT queries and returns DataTable
- InitializeDatabase() - Creates database and tables if they don't exist
- CreateTables() - Creates all database tables with proper constraints
- SeedData() - Populates initial data (components and computers)
- CustomerDataAccess.cs: Customer CRUD operations
- ComputerDataAccess.cs: Computer retrieval operations
- ComponentDataAccess.cs: Component retrieval operations
- OrderDataAccess.cs: Order CRUD operations including order item management
Note: I chose ADO.NET over Entity Framework because I wanted more direct control over the SQL queries. It was more work, but I feel like I understand what's happening under the hood better. The DatabaseHelper class centralizes all database operations, which makes it easier to maintain and debug. I also made the database initialization automatic - it creates everything on first run, which is convenient for deployment.
Web Pages and Functionality
Account Pages
Account/Login.aspx - User Login
Functionality: User authentication with email and password. Validates credentials against database using SHA256 password hashing. Updates LastLoginDate upon successful login. Creates session variables for authenticated users.
Account/Register.aspx - User Registration
Functionality: New customer account creation. Validates email uniqueness. Hashes password using SHA256 before storage. Creates customer record in database with CreatedDate timestamp.
Note: The registration form has validation on both the client side and server side. I check if the email is already taken by querying the database before making the account. The password confirmation field helps catch typos. I set CreatedDate automatically using DateTime.Now, which is way more reliable than trusting the user's computer clock.
Account/Profile.aspx - Customer Profile Management
Functionality: View and update customer profile information. Optional password change. Updates customer data in database and session variables.
Account/ForgotPassword.aspx - Password Recovery
Functionality: Password recovery functionality for customers who have forgotten their password.
Store Pages
Store/Products.aspx - Product Catalog
Functionality: Displays all available computers from database. Shows default component configurations. Provides "Configure & Buy" button for each computer.
Store/Configure.aspx - Computer Configuration
Functionality: Allows customization of computer components. Dynamic price calculation using AJAX. Real-time total price updates. Add configured computer to cart.
Note: This page is basically the same as Part 3, but now it reads from the database instead of StaticData. The AJAX calls are the same, which made moving from Part 3 to Part 4 way easier. I kept the same user experience - users shouldn't really notice a difference except the data can be updated in the database now.
Store/Cart.aspx - Shopping Cart
Functionality: Displays cart items from browser cookie. Calculate total price. Remove items from cart. Proceed to checkout (requires login).
Store/Checkout.aspx - Order Checkout
Functionality: Collects shipping and payment information. Creates order record in database. Creates order items for each cart item. Clears cart after successful order creation. Redirects to order details page.
Note: The checkout process creates both the order and all order items together (well, as close as I could get with ADO.NET). I store the component configuration as JSON in each order item, which saves exactly what the customer picked. This matters because component prices might change later, but we want to keep the original order prices.
Orders Pages
Orders/MyOrders.aspx - Order History
Functionality: Displays all orders for logged-in customer. Shows order date, status (with color-coded badges), and total amount. Provides link to view order details.
Note: I used Bootstrap badges for the order status - green for "Completed", yellow for "Processing", red for "Cancelled", stuff like that. Makes it super easy to see what's going on. The orders are sorted by date with newest first, which seems like the most useful way. I only show orders for the logged-in customer - gotta keep things secure.
Orders/OrderDetails.aspx - Order Details View
Functionality: Displays comprehensive order information. Shows order items with component configurations. Validates order ownership (security check). Provides links to edit or delete order.
Orders/EditOrder.aspx - Order Editing
Functionality: Allows editing of order items (component selection and prices). Order status is read-only. Updates order and order items in database. Recalculates total order amount.
Order Editing Flow
flowchart TD
A[User Views Order] --> B[Load Order Items from Database]
B --> C[Display Components in Dropdowns]
C --> D{User Changes Component?}
D -->|Yes| E[AJAX Call Get New Price]
D -->|No| F{User Clicks Save Changes?}
E --> G[Recalculate Item Total Update Display]
G --> H[Recalculate Order Total Show New Total]
H --> F
F -->|No| I[Cancel]
F -->|Yes| J[Update Database]
J --> K[Update Order Items Update Order Total]
K --> L[Reload Page Show Updated Order]
Note: The EditOrder page was one of the more complex pages to implement. I had to make sure the dynamic price calculation worked correctly when components were changed, and that the total order amount was recalculated properly. I made the order status read-only because I wanted to keep that as an administrative function (though I didn't implement an admin interface). The component dropdowns are populated dynamically, which required careful handling of the Repeater control and JavaScript integration.
Security Features
Authentication Security
- Password Hashing: SHA256 hashing for password storage
- Session Management: Secure session-based authentication
- Authorization: Pages requiring authentication check session and redirect to login if needed
- Order Access Control: Customers can only view/edit there own orders
Note: I used SHA256 for password hashing, which is better than plain text but I know that for production systems, you'd want to use bcrypt or Argon2 with salt. For this assignment, SHA256 was sufficient. I made sure to check order ownership on every page that displays order details - it's important to prevent users from accessing other people's orders. You can't just trust the URL parameters!
Data Security
- SQL Injection Prevention: All database queries use parameterized SQL statements
- Input Validation: Client-side and server-side validation on all forms
- Email Uniqueness: Database-level unique constraint on Email column
Database Initialization
Automatic Database Setup
The database is automatically initialized when the application starts:
- Trigger: Application_Start event in Global.asax.cs calls DatabaseHelper.InitializeDatabase()
- Process: Creates database if it doesn't exist, creates all tables, seeds initial data
- Idempotent: Can be called multiple times safely
Note: The automatic database initialization was a lifesaver during development. I could delete the database files and restart the app, and everything would be recreated. The IF NOT EXISTS checks in the SQL scripts make it safe to run multiple times. I used LocalDB because it's easy to set up and doesn't require a full SQL Server installation. The database files are stored in App_Data, which makes it easy to back up or reset.
Configuration
Connection String
<connectionStrings>
<add name="DefaultConnection"
connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\ComputerStore.mdf;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
Note: LocalDB is pretty sweet for development - it's a lightweight version of SQL Server that you don't need to fully install. The |DataDirectory| thing automatically points to the App_Data folder, which is handy. Integrated Security=True means it uses Windows authentication, which is way simpler for local development. For production, you'd want to use SQL Server authentication with a dedicated user account.
User Guide
Setup Instructions
- Open the solution file (TM3A.sln) in Visual Studio
- Restore NuGet packages (right-click solution → Restore NuGet Packages)
- Build the solution (Build → Build Solution or Ctrl+Shift+B)
- Set part4 as the startup project (right-click part4 project → Set as StartUp Project)
- Run the application (F5 or Debug → Start Debugging)
- Database will be created automatically on first run in App_Data folder
- Initial data (components and computers) will be seeded automatically
Using the Application
- Registration: Create a new account at /Account/Register.aspx
- Login: Log in with your credentials at /Account/Login.aspx
- Browse Products: View available computers at /Store/Products.aspx
- Configure Computer: Customize components at /Store/Configure.aspx
- Add to Cart: Add configured computers to shopping cart
- View Cart: Review cart items at /Store/Cart.aspx
- Checkout: Complete order at /Store/Checkout.aspx (requires login)
- View Orders: See order history at /Orders/MyOrders.aspx
- Order Details: View detailed order information at /Orders/OrderDetails.aspx
- Edit Order: Modify order items at /Orders/EditOrder.aspx
- Profile Management: Update account information at /Account/Profile.aspx
Accessing the Application
Technical Implementation Details
Password Hashing Implementation
private string HashPassword(string password)
{
using (SHA256 sha256Hash = SHA256.Create())
{
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(password));
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
return builder.ToString();
}
}
JSON Serialization for Component Configurations
The application uses JSON to store component configurations in the database:
{"CPU":"cpu-i7","RAM":"ram-16","Storage":"ssd-512","GPU":"gpu-gtx1660",
"Monitor":"monitor-none","Power Supply":"psu-750w","Case":"case-atx",
"Operating System":"os-windows","Sound Card":"sound-logitech"}
Note: Storing component configurations as JSON is pretty flexible - we can add new component categories without messing with the database schema. I use Newtonsoft.Json for serialization, which is the go-to library for this. The JSON is stored as NVARCHAR in the database, which works fine for this. If I needed to query by specific components, I'd have to rethink this, but for now it works great.
Parameterized Query Example
All database queries use parameterized statements to prevent SQL injection:
var sql = "SELECT Id, FirstName, LastName, Email FROM Customers WHERE Email = @Email AND IsActive = 1";
var dataTable = DatabaseHelper.ExecuteDataTable(sql, new SqlParameter("@Email", email));
Note: I made sure to use parameterized queries everywhere - keeps SQL injection attacks from happening. I used SqlParameter objects for all user input, which automatically handles escaping and type conversion stuff.
Key Design Decisions
- ADO.NET over Entity Framework: Chosen for direct SQL control and performance
- Cookie-based Cart: Cart stored in browser cookies allows persistence accross sessions without requiring login
- JSON for Complex Data: Component configurations stored as JSON allows flexible schema without additional tables
- Folder Organization: Pages organized into logical folders for better maintainability
- SHA256 Password Hashing: Industry-standard hashing algorithm for password storage
- Session-based Authentication: Simple and effective for this application scope
- Automatic Database Initialization: Database and tables created automatically on first run for easy deployment
Note to Instructor: This is a complete e-commerce application demonstrating proper use of ADO.NET data access, session management, and database design principles. The application includes comprehensive customer management, order tracking, and security features. All database operations use parameterized queries for security, and the application automatically initializes the database structure on first run.
Personal note: Part 4 was definitely the hardest part of the whole assignment. Moving from hardcoded data to a real database was a big jump, and I had to learn a ton about ADO.NET, SQL Server, and database design. The most time-consuming part was getting all the order management stuff working right - especially the EditOrder page with dynamic price recalculation. That was a pain. I also spent way too much time debugging connection string issues and making sure the database initialization actually worked. Overall, I'm pretty happy with how it turned out, though there's definitely stuff I'd improve if I had more time (like adding an admin interface, better error handling, and more customization options). The progression from Part 1 to Part 4 really shows how web apps can get complicated fast!
Summary
This assignment showcases multiple sites using ASP.NET Web Forms:
- Part 1: Basic visitor tracking with cookies and client-side JavaScript
- Part 2: Interactive slideshow with file I/O and dynamic UI updates
- Part 3: Initial e-commerce application with hardcoded data and cookie-based cart (foundation for Part 4)
- Part 4: Extended and advanced e-commerce system built upon Part 3, adding database integration, user authentication, and comprehensive order management
All parts use consistent Bootstrap styling and follow ASP.NET Web Forms best practices. The progression demonstrates the evolution of web application complexity: starting with basic visitor tracking, moving to interactive slideshows, then building an initial e-commerce application (Part 3), and finally extending it into a full-featured database-driven system (Part 4)..
All Applications Accessible From: