4. Basic Data Layer

This iteration we’ll set up a basic data layer comprising: a single data model, a DbContext and a PostgreSQL database running in Docker.

Maps to iteration_01 in the Run1API Repo

About this chapter

Structuring your data for APIs:

  • Domain models: Entity classes with relationships and validation
  • DTOs (Data Transfer Objects): Contracts for API communication
  • Why separation matters: Security, versioning, and over-posting prevention
  • C# features: Required members, nullable reference types, navigation properties
  • Best practices: Conventions and proper property naming

Learning outcomes:

  • Create domain models with relationships and constraints
  • Understand the DTO pattern and why it’s essential for APIs
  • Design DTOs for create, read, and update operations
  • Apply C# naming conventions and validation attributes

3.1 Creating Domain Models

namespace CommandAPI.Models;

public class Platform
{    
  [Key]    
  public int Id { get; set; }    
  
  [Required]    
  public required string PlatformName { get; set; }    
  
  // Navigation property    
  public ICollection<Command> Commands { get; set; } = new List<Command>();    
  public string? CreatedBy { get; set; }
}
  • Required Members (C# 11+): required keyword enforces initialization
  • Nullable Reference Types: string? vs string
  • Navigation Properties: EF Core uses these for relationships
  • Collection Initialization: Best practice to avoid null reference issues

3.2 Understanding DTOs and Why We Need Them

  • DTO = Data Transfer Object: Contracts for API communication
  • Why Not Expose Models Directly?:
    • Security: Hide sensitive fields (passwords, internal IDs)
    • Versioning: API contract independent of database schema
    • Over-posting: Prevent clients from setting unauthorized fields
    • Performance: Return only needed data
    • Circular References: Avoid JSON serialization issues

3.3 CORRECTION: Proper C# Naming Conventions

// ❌ WRONG (current code has this issue)
public class AppDbContext : DbContext
{    
  public DbSet<Platform> platforms { get; set; }  // camelCase    
  public DbSet<Command> commands { get; set; }
}

// ✅ CORRECT
public class AppDbContext : DbContext
{    
  public DbSet<Platform> Platforms { get; set; }  // PascalCase    
  public DbSet<Command> Commands { get; set; }    
  public DbSet<KeyRegistration> KeyRegistrations { get; set; }
  public DbSet<BulkJobResult> BulkJobResults { get; set; }
}
  • C# Naming Conventions:
    • Classes, Properties, Methods: PascalCase
    • Local variables, parameters: camelCase
    • Private fields: _camelCase (with underscore)
    • Constants: UPPER_SNAKE_CASE or PascalCase

3.4 Data Annotations for Basic Validation

public class PlatformMutateDto
{    
  [Required(ErrorMessage = "Platform name is required")]   
  [MaxLength(100, ErrorMessage = "Platform name cannot exceed 100 characters")]    
  [MinLength(2, ErrorMessage = "Platform name must be at least 2 characters")]public required string PlatformName { get; init; }
}

public class CommandMutateDto
{    
  [Required]    
  [MaxLength(250)]    
  public required string HowTo { get; init; }    
  
  [Required]    
  public required string CommandLine { get; init; }    
  
  [Range(1, int.MaxValue, ErrorMessage = "Valid Platform ID required")]    
  public int PlatformId { get; set; }
}
  • Common Annotations:
    • [Required] - non-null, non-empty
    • [MaxLength(n)] - string/array length
    • [Range(min, max)] - numeric bounds
    • [EmailAddress], [Url], [Phone] - format validation
    • [RegularExpression(pattern)] - custom patterns

3.5 The Separation of Concerns Principle

┌─────────────────┐     ┌──────────────┐     ┌────────────────┐
│   Controller    │────▶│  Repository  │────▶│   DbContext    │
│  (HTTP Logic)   │     │ (Data Logic) │     │  (EF Core)     │
└─────────────────┘     └──────────────┘     └────────────────┘
        │                                              │ 
        ▼                                              ▼  
   ┌─────────┐                                  ┌──────────┐   
   │   DTO   │                                  │  Models  │  
   └─────────┘                                  └──────────┘   
       │                                              │        
       └────────────────┬─────────────────────────────┘     
                        ▼                  
                 ┌────────────┐  
                 │ AutoMapper │                   
                 └────────────┘
  • Layers Explained:
    • Controller: Handles HTTP, validates input, returns responses
    • Repository: Data access logic, queries, business rules
    • DbContext: EF Core abstraction over database
    • Models: Database entities
    • DTOs: API contracts
    • AutoMapper: Transforms between Models and DTOs