WIP: Add EF Core entity models with navigation properties and Fluent API configurations #69

Closed
Copilot wants to merge 2 commits from copilot/create-ef-core-entity-models into main
Copilot commented 2026-01-19 09:58:48 +00:00 (Migrated from github.com)

Part of the EF Core migration epic (#58). Creates proper entity classes to replace current record models, establishing the foundation for EF Core integration.

Entities Created

5 entity classes in Data/Entities/:

  • Feed - Podcast feed with YouTube configuration and navigation properties to Episodes, DownloadedVideos, ActivityLogs, DownloadQueue
  • Episode - Episode with FeedId foreign key and back-reference to Feed
  • DownloadedVideo - Downloaded video tracking with FeedId foreign key
  • ActivityLog - Activity history with nullable FeedId for system-wide events
  • DownloadQueueItem - Active download queue with FeedId foreign key

All entities use [Required], [MaxLength] annotations and proper nullable reference types.

Fluent API Configurations

5 configuration classes in Data/Configurations/ implementing IEntityTypeConfiguration<T>:

  • Table name mappings match existing schema (feeds, episodes, downloaded_videos, activity_log, download_queue)
  • 11 indexes including unique constraints on Feed.Name, Episode(FeedId, Filename), DownloadedVideo(FeedId, VideoId), DownloadQueueItem(FeedId, VideoId)
  • Cascade delete for all Feed relationships

DbContext

CastrDbContext with 5 DbSets and automatic configuration discovery via ApplyConfigurationsFromAssembly().

public class CastrDbContext : DbContext
{
    public DbSet<Feed> Feeds => Set<Feed>();
    public DbSet<Episode> Episodes => Set<Episode>();
    // ... other DbSets
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(CastrDbContext).Assembly);
    }
}

Package References

Added EF Core 9.0.0 packages: EntityFrameworkCore, EntityFrameworkCore.Sqlite, EntityFrameworkCore.Design.

Schema alignment verified against existing CentralDatabaseService SQL definitions.

Original prompt

This section details on the original issue you should resolve

<issue_title>Create EF Core entity models with relationships</issue_title>
<issue_description>## Parent Epic
Part of joezombie/btb_pod#58 - Migrate to Entity Framework Core

Description

Create proper EF Core entity classes to replace the current record models, with navigation properties and data annotations.

Entity Models

Feed Entity

// Data/Entities/Feed.cs
public class Feed
{
    public int Id { get; set; }
    
    [Required, MaxLength(100)]
    public string Name { get; set; } = string.Empty;
    
    [Required, MaxLength(500)]
    public string Title { get; set; } = string.Empty;
    
    [Required]
    public string Description { get; set; } = string.Empty;
    
    [Required, MaxLength(1000)]
    public string Directory { get; set; } = string.Empty;
    
    [MaxLength(200)]
    public string? Author { get; set; }
    
    [MaxLength(1000)]
    public string? ImageUrl { get; set; }
    
    [MaxLength(1000)]
    public string? Link { get; set; }
    
    [MaxLength(10)]
    public string Language { get; set; } = "en-us";
    
    [MaxLength(100)]
    public string? Category { get; set; }
    
    [MaxLength(100)]
    public string FileExtensions { get; set; } = ".mp3";
    
    // YouTube configuration
    [MaxLength(500)]
    public string? YouTubePlaylistUrl { get; set; }
    public int YouTubePollIntervalMinutes { get; set; } = 60;
    public bool YouTubeEnabled { get; set; }
    public int YouTubeMaxConcurrentDownloads { get; set; } = 1;
    [MaxLength(20)]
    public string YouTubeAudioQuality { get; set; } = "highest";
    
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
    public bool IsActive { get; set; } = true;
    
    // Navigation properties
    public ICollection<Episode> Episodes { get; set; } = new List<Episode>();
    public ICollection<DownloadedVideo> DownloadedVideos { get; set; } = new List<DownloadedVideo>();
    public ICollection<ActivityLog> ActivityLogs { get; set; } = new List<ActivityLog>();
    public ICollection<DownloadQueueItem> DownloadQueue { get; set; } = new List<DownloadQueueItem>();
}

Episode Entity

// Data/Entities/Episode.cs
public class Episode
{
    public int Id { get; set; }
    public int FeedId { get; set; }
    
    [Required, MaxLength(500)]
    public string Filename { get; set; } = string.Empty;
    
    [MaxLength(50)]
    public string? VideoId { get; set; }
    
    [MaxLength(500)]
    public string? YoutubeTitle { get; set; }
    
    public string? Description { get; set; }
    
    [MaxLength(1000)]
    public string? ThumbnailUrl { get; set; }
    
    public int DisplayOrder { get; set; }
    public DateTime AddedAt { get; set; }
    public DateTime? PublishDate { get; set; }
    public double? MatchScore { get; set; }
    
    // Navigation property
    public Feed Feed { get; set; } = null!;
}

Other Entities

  • DownloadedVideo - Tracks downloaded YouTube videos
  • ActivityLog - Activity history for monitoring
  • DownloadQueueItem - Active download queue

Entity Configurations

Use Fluent API for complex configurations:

// Data/Configurations/EpisodeConfiguration.cs
public class EpisodeConfiguration : IEntityTypeConfiguration<Episode>
{
    public void Configure(EntityTypeBuilder<Episode> builder)
    {
        builder.HasIndex(e => new { e.FeedId, e.Filename }).IsUnique();
        builder.HasIndex(e => e.VideoId);
        builder.HasIndex(e => new { e.FeedId, e.DisplayOrder });
        
        builder.HasOne(e => e.Feed)
            .WithMany(f => f.Episodes)
            .HasForeignKey(e => e.FeedId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}

Tasks

  • Create Data/Entities/ folder
  • Implement Feed entity with all properties
  • Implement Episode entity with relationship
  • Implement DownloadedVideo entity
  • Implement ActivityLog entity
  • Implement DownloadQueueItem entity
  • Create configuration classes in Data/Configurations/
  • Update DbContext to use new entities
  • Add proper indexes and constraints

Acceptance Criteria

  • All entities have proper data annotations
  • Relationships defined with navigation properties
  • Indexes match current database schema
  • Foreign key cascades configured correctly</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Part of the EF Core migration epic (#58). Creates proper entity classes to replace current record models, establishing the foundation for EF Core integration. ## Entities Created **5 entity classes** in `Data/Entities/`: - `Feed` - Podcast feed with YouTube configuration and navigation properties to Episodes, DownloadedVideos, ActivityLogs, DownloadQueue - `Episode` - Episode with FeedId foreign key and back-reference to Feed - `DownloadedVideo` - Downloaded video tracking with FeedId foreign key - `ActivityLog` - Activity history with nullable FeedId for system-wide events - `DownloadQueueItem` - Active download queue with FeedId foreign key All entities use `[Required]`, `[MaxLength]` annotations and proper nullable reference types. ## Fluent API Configurations **5 configuration classes** in `Data/Configurations/` implementing `IEntityTypeConfiguration<T>`: - Table name mappings match existing schema (`feeds`, `episodes`, `downloaded_videos`, `activity_log`, `download_queue`) - **11 indexes** including unique constraints on `Feed.Name`, `Episode(FeedId, Filename)`, `DownloadedVideo(FeedId, VideoId)`, `DownloadQueueItem(FeedId, VideoId)` - Cascade delete for all Feed relationships ## DbContext `CastrDbContext` with 5 DbSets and automatic configuration discovery via `ApplyConfigurationsFromAssembly()`. ```csharp public class CastrDbContext : DbContext { public DbSet<Feed> Feeds => Set<Feed>(); public DbSet<Episode> Episodes => Set<Episode>(); // ... other DbSets protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(CastrDbContext).Assembly); } } ``` ## Package References Added EF Core 9.0.0 packages: `EntityFrameworkCore`, `EntityFrameworkCore.Sqlite`, `EntityFrameworkCore.Design`. Schema alignment verified against existing `CentralDatabaseService` SQL definitions. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Create EF Core entity models with relationships</issue_title> > <issue_description>## Parent Epic > Part of joezombie/btb_pod#58 - Migrate to Entity Framework Core > > ## Description > Create proper EF Core entity classes to replace the current record models, with navigation properties and data annotations. > > ## Entity Models > > ### Feed Entity > ```csharp > // Data/Entities/Feed.cs > public class Feed > { > public int Id { get; set; } > > [Required, MaxLength(100)] > public string Name { get; set; } = string.Empty; > > [Required, MaxLength(500)] > public string Title { get; set; } = string.Empty; > > [Required] > public string Description { get; set; } = string.Empty; > > [Required, MaxLength(1000)] > public string Directory { get; set; } = string.Empty; > > [MaxLength(200)] > public string? Author { get; set; } > > [MaxLength(1000)] > public string? ImageUrl { get; set; } > > [MaxLength(1000)] > public string? Link { get; set; } > > [MaxLength(10)] > public string Language { get; set; } = "en-us"; > > [MaxLength(100)] > public string? Category { get; set; } > > [MaxLength(100)] > public string FileExtensions { get; set; } = ".mp3"; > > // YouTube configuration > [MaxLength(500)] > public string? YouTubePlaylistUrl { get; set; } > public int YouTubePollIntervalMinutes { get; set; } = 60; > public bool YouTubeEnabled { get; set; } > public int YouTubeMaxConcurrentDownloads { get; set; } = 1; > [MaxLength(20)] > public string YouTubeAudioQuality { get; set; } = "highest"; > > public DateTime CreatedAt { get; set; } > public DateTime UpdatedAt { get; set; } > public bool IsActive { get; set; } = true; > > // Navigation properties > public ICollection<Episode> Episodes { get; set; } = new List<Episode>(); > public ICollection<DownloadedVideo> DownloadedVideos { get; set; } = new List<DownloadedVideo>(); > public ICollection<ActivityLog> ActivityLogs { get; set; } = new List<ActivityLog>(); > public ICollection<DownloadQueueItem> DownloadQueue { get; set; } = new List<DownloadQueueItem>(); > } > ``` > > ### Episode Entity > ```csharp > // Data/Entities/Episode.cs > public class Episode > { > public int Id { get; set; } > public int FeedId { get; set; } > > [Required, MaxLength(500)] > public string Filename { get; set; } = string.Empty; > > [MaxLength(50)] > public string? VideoId { get; set; } > > [MaxLength(500)] > public string? YoutubeTitle { get; set; } > > public string? Description { get; set; } > > [MaxLength(1000)] > public string? ThumbnailUrl { get; set; } > > public int DisplayOrder { get; set; } > public DateTime AddedAt { get; set; } > public DateTime? PublishDate { get; set; } > public double? MatchScore { get; set; } > > // Navigation property > public Feed Feed { get; set; } = null!; > } > ``` > > ### Other Entities > - `DownloadedVideo` - Tracks downloaded YouTube videos > - `ActivityLog` - Activity history for monitoring > - `DownloadQueueItem` - Active download queue > > ## Entity Configurations > > Use Fluent API for complex configurations: > > ```csharp > // Data/Configurations/EpisodeConfiguration.cs > public class EpisodeConfiguration : IEntityTypeConfiguration<Episode> > { > public void Configure(EntityTypeBuilder<Episode> builder) > { > builder.HasIndex(e => new { e.FeedId, e.Filename }).IsUnique(); > builder.HasIndex(e => e.VideoId); > builder.HasIndex(e => new { e.FeedId, e.DisplayOrder }); > > builder.HasOne(e => e.Feed) > .WithMany(f => f.Episodes) > .HasForeignKey(e => e.FeedId) > .OnDelete(DeleteBehavior.Cascade); > } > } > ``` > > ## Tasks > - [ ] Create `Data/Entities/` folder > - [ ] Implement Feed entity with all properties > - [ ] Implement Episode entity with relationship > - [ ] Implement DownloadedVideo entity > - [ ] Implement ActivityLog entity > - [ ] Implement DownloadQueueItem entity > - [ ] Create configuration classes in `Data/Configurations/` > - [ ] Update DbContext to use new entities > - [ ] Add proper indexes and constraints > > ## Acceptance Criteria > - [ ] All entities have proper data annotations > - [ ] Relationships defined with navigation properties > - [ ] Indexes match current database schema > - [ ] Foreign key cascades configured correctly</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes joezombie/btb_pod#60 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
joezombie (Migrated from github.com) reviewed 2026-01-19 09:58:48 +00:00

Pull request closed

Sign in to join this conversation.
No description provided.