Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions ECommerceDataModel/Data/ECommerceDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using Microsoft.EntityFrameworkCore;
using ECommerceDataModel.Models;

namespace ECommerceDataModel.Data;

public class ECommerceDbContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }

public ECommerceDbContext(DbContextOptions<ECommerceDbContext> options) : base(options)
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

// Configure Category entity
modelBuilder.Entity<Category>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.Name).IsUnique();
entity.Property(e => e.Name).IsRequired().HasMaxLength(100);
entity.Property(e => e.Description).HasMaxLength(500);
});

// Configure Product entity
modelBuilder.Entity<Product>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.SKU).IsUnique();
entity.Property(e => e.Name).IsRequired().HasMaxLength(200);
entity.Property(e => e.Description).HasMaxLength(1000);
entity.Property(e => e.Price).HasColumnType("decimal(18,2)").IsRequired();
entity.Property(e => e.StockQuantity).IsRequired();
entity.Property(e => e.SKU).HasMaxLength(50);
entity.Property(e => e.IsActive).HasDefaultValue(true);
entity.Property(e => e.CreatedDate).HasDefaultValueSql("GETUTCDATE()");

// Foreign key relationship
entity.HasOne(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId)
.OnDelete(DeleteBehavior.Restrict);
});

// Configure Customer entity
modelBuilder.Entity<Customer>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.Email).IsUnique();
entity.Property(e => e.FirstName).IsRequired().HasMaxLength(100);
entity.Property(e => e.LastName).IsRequired().HasMaxLength(100);
entity.Property(e => e.Email).IsRequired().HasMaxLength(255);
entity.Property(e => e.Phone).HasMaxLength(20);
entity.Property(e => e.Address).HasMaxLength(255);
entity.Property(e => e.City).HasMaxLength(100);
entity.Property(e => e.PostalCode).HasMaxLength(10);
entity.Property(e => e.Country).HasMaxLength(100);
entity.Property(e => e.CreatedDate).HasDefaultValueSql("GETUTCDATE()");
});

// Configure Order entity
modelBuilder.Entity<Order>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.OrderNumber).IsUnique();
entity.Property(e => e.OrderNumber).IsRequired().HasMaxLength(50);
entity.Property(e => e.OrderDate).HasDefaultValueSql("GETUTCDATE()");
entity.Property(e => e.TotalAmount).HasColumnType("decimal(18,2)").IsRequired();
entity.Property(e => e.Status).IsRequired().HasMaxLength(50).HasDefaultValue("Pending");
entity.Property(e => e.ShippingAddress).HasMaxLength(255);
entity.Property(e => e.ShippingCity).HasMaxLength(100);
entity.Property(e => e.ShippingPostalCode).HasMaxLength(10);
entity.Property(e => e.ShippingCountry).HasMaxLength(100);

// Foreign key relationship
entity.HasOne(o => o.Customer)
.WithMany(c => c.Orders)
.HasForeignKey(o => o.CustomerId)
.OnDelete(DeleteBehavior.Restrict);
});

// Configure OrderItem entity
modelBuilder.Entity<OrderItem>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Quantity).IsRequired();
entity.Property(e => e.UnitPrice).HasColumnType("decimal(18,2)").IsRequired();
entity.Property(e => e.TotalPrice).HasColumnType("decimal(18,2)").IsRequired();

// Foreign key relationships
entity.HasOne(oi => oi.Order)
.WithMany(o => o.OrderItems)
.HasForeignKey(oi => oi.OrderId)
.OnDelete(DeleteBehavior.Cascade);

entity.HasOne(oi => oi.Product)
.WithMany(p => p.OrderItems)
.HasForeignKey(oi => oi.ProductId)
.OnDelete(DeleteBehavior.Restrict);

// Composite index for performance
entity.HasIndex(e => new { e.OrderId, e.ProductId });
});

// Seed data
SeedData(modelBuilder);
}

private static void SeedData(ModelBuilder modelBuilder)
{
// Seed Categories
modelBuilder.Entity<Category>().HasData(
new Category { Id = 1, Name = "Electronics", Description = "Electronic devices and accessories" },
new Category { Id = 2, Name = "Clothing", Description = "Fashion and apparel" },
new Category { Id = 3, Name = "Books", Description = "Books and educational materials" }
);

// Seed Products
modelBuilder.Entity<Product>().HasData(
new Product { Id = 1, Name = "Laptop", Description = "High-performance laptop", Price = 999.99m, StockQuantity = 50, SKU = "LAP001", CategoryId = 1 },
new Product { Id = 2, Name = "Smartphone", Description = "Latest smartphone model", Price = 699.99m, StockQuantity = 100, SKU = "PHN001", CategoryId = 1 },
new Product { Id = 3, Name = "T-Shirt", Description = "Cotton t-shirt", Price = 19.99m, StockQuantity = 200, SKU = "TSH001", CategoryId = 2 },
new Product { Id = 4, Name = "Programming Book", Description = "Learn C# programming", Price = 49.99m, StockQuantity = 75, SKU = "BK001", CategoryId = 3 }
);

// Seed Customer
modelBuilder.Entity<Customer>().HasData(
new Customer { Id = 1, FirstName = "John", LastName = "Doe", Email = "john.doe@example.com", Phone = "123-456-7890", Address = "123 Main St", City = "Anytown", PostalCode = "12345", Country = "USA" }
);
}
}
20 changes: 20 additions & 0 deletions ECommerceDataModel/ECommerceDataModel.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

</Project>
70 changes: 70 additions & 0 deletions ECommerceDataModel/ERD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# eCommerce Data Model - Entity Relationship Diagram

```
┌─────────────────┐ ┌─────────────────┐
│ Category │ │ Customer │
├─────────────────┤ ├─────────────────┤
│ Id (PK) │ │ Id (PK) │
│ Name │ │ FirstName │
│ Description │ │ LastName │
└─────────────────┘ │ Email │
│ │ Phone │
│ 1:N │ Address │
▼ │ City │
┌─────────────────┐ │ PostalCode │
│ Product │ │ Country │
├─────────────────┤ │ CreatedDate │
│ Id (PK) │ └─────────────────┘
│ Name │ │
│ Description │ │ 1:N
│ Price │ ▼
│ StockQuantity │ ┌─────────────────┐
│ SKU │ │ Order │
│ IsActive │ ├─────────────────┤
│ CreatedDate │ │ Id (PK) │
│ CategoryId (FK) │ │ OrderNumber │
└─────────────────┘ │ OrderDate │
│ │ TotalAmount │
│ N:M │ Status │
│ (via OrderItem) │ ShippingAddress │
▼ │ ShippingCity │
┌─────────────────┐ │ ShippingPostalCode │
│ OrderItem │ │ ShippingCountry │
├─────────────────┤ │ ShippedDate │
│ Id (PK) │ │ DeliveredDate │
│ Quantity │ │ CustomerId (FK) │
│ UnitPrice │ └─────────────────┘
│ TotalPrice │ ▲
│ OrderId (FK) │ │ 1:N
│ ProductId (FK) │ │
└─────────────────┘──────────────────┘
```

## Relationships:

1. **Category → Product** (1:N)
- One category can have many products
- Each product belongs to one category

2. **Customer → Order** (1:N)
- One customer can place many orders
- Each order belongs to one customer

3. **Order → OrderItem** (1:N)
- One order can have many order items
- Each order item belongs to one order

4. **Product → OrderItem** (1:N)
- One product can appear in many order items
- Each order item references one product

5. **Product ↔ Order** (N:M via OrderItem)
- Many-to-many relationship through OrderItem junction table
- Allows tracking quantity and pricing per order

## Key Constraints:

- **Unique Indexes**: Category.Name, Product.SKU, Customer.Email, Order.OrderNumber
- **Foreign Key Constraints**: All relationships enforced with restrict/cascade rules
- **Data Validation**: Required fields, max lengths, email format validation
- **Default Values**: Timestamps, boolean flags, status fields
18 changes: 18 additions & 0 deletions ECommerceDataModel/Models/Category.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;

namespace ECommerceDataModel.Models;

public class Category
{
public int Id { get; set; }

[Required]
[MaxLength(100)]
public string Name { get; set; } = string.Empty;

[MaxLength(500)]
public string? Description { get; set; }

// Navigation property
public virtual ICollection<Product> Products { get; set; } = new List<Product>();
}
41 changes: 41 additions & 0 deletions ECommerceDataModel/Models/Customer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.ComponentModel.DataAnnotations;

namespace ECommerceDataModel.Models;

public class Customer
{
public int Id { get; set; }

[Required]
[MaxLength(100)]
public string FirstName { get; set; } = string.Empty;

[Required]
[MaxLength(100)]
public string LastName { get; set; } = string.Empty;

[Required]
[MaxLength(255)]
[EmailAddress]
public string Email { get; set; } = string.Empty;

[MaxLength(20)]
public string? Phone { get; set; }

[MaxLength(255)]
public string? Address { get; set; }

[MaxLength(100)]
public string? City { get; set; }

[MaxLength(10)]
public string? PostalCode { get; set; }

[MaxLength(100)]
public string? Country { get; set; }

public DateTime CreatedDate { get; set; } = DateTime.UtcNow;

// Navigation property
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
}
46 changes: 46 additions & 0 deletions ECommerceDataModel/Models/Order.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ECommerceDataModel.Models;

public class Order
{
public int Id { get; set; }

[Required]
[MaxLength(50)]
public string OrderNumber { get; set; } = string.Empty;

public DateTime OrderDate { get; set; } = DateTime.UtcNow;

[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal TotalAmount { get; set; }

[Required]
[MaxLength(50)]
public string Status { get; set; } = "Pending"; // Pending, Processing, Shipped, Delivered, Cancelled

[MaxLength(255)]
public string? ShippingAddress { get; set; }

[MaxLength(100)]
public string? ShippingCity { get; set; }

[MaxLength(10)]
public string? ShippingPostalCode { get; set; }

[MaxLength(100)]
public string? ShippingCountry { get; set; }

public DateTime? ShippedDate { get; set; }

public DateTime? DeliveredDate { get; set; }

// Foreign key
public int CustomerId { get; set; }

// Navigation properties
public virtual Customer Customer { get; set; } = null!;
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}
28 changes: 28 additions & 0 deletions ECommerceDataModel/Models/OrderItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ECommerceDataModel.Models;

public class OrderItem
{
public int Id { get; set; }

[Required]
public int Quantity { get; set; }

[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal UnitPrice { get; set; }

[Required]
[Column(TypeName = "decimal(18,2)")]
public decimal TotalPrice { get; set; }

// Foreign keys
public int OrderId { get; set; }
public int ProductId { get; set; }

// Navigation properties
public virtual Order Order { get; set; } = null!;
public virtual Product Product { get; set; } = null!;
}
Loading