From 3a809f18a6ae856e5cace86b7df58dcf6d93aff2 Mon Sep 17 00:00:00 2001 From: James Kolpack Date: Sat, 27 Dec 2025 15:59:48 -0500 Subject: [PATCH] Add EventOccurrence entity and update DbContext Created the EventOccurrence entity with properties for event details and added it to the AppDbContext. Updated the model snapshot to reflect the new entity and its relationships, ensuring proper database schema generation. --- Core/Entities/EventOccurrence.cs | 14 +- Data/AppDbContext.cs | 1 + .../EventOccurrenceConfiguration.cs | 48 +++ ...20251227205816_EventOccurrence.Designer.cs | 339 ++++++++++++++++++ .../20251227205816_EventOccurrence.cs | 63 ++++ Data/Migrations/AppDbContextModelSnapshot.cs | 60 ++++ 6 files changed, 522 insertions(+), 3 deletions(-) create mode 100644 Data/Configurations/EventOccurrenceConfiguration.cs create mode 100644 Data/Migrations/20251227205816_EventOccurrence.Designer.cs create mode 100644 Data/Migrations/20251227205816_EventOccurrence.cs diff --git a/Core/Entities/EventOccurrence.cs b/Core/Entities/EventOccurrence.cs index c171b18..1bf82d3 100644 --- a/Core/Entities/EventOccurrence.cs +++ b/Core/Entities/EventOccurrence.cs @@ -1,14 +1,22 @@ - -namespace Core.Entities +namespace Core.Entities { public class EventOccurrence { + public int Id { get; set; } + + public int? EventDefinitionId { get; set; } + + public string? SpecialEventType { get; set; } + public string Name { get; set; } = null!; public string Time { get; set; } = null!; public string Date { get; set; } = null!; public DateTime StartTime { get; set; } public DateTime? EndTime { get; set; } - public string Location { get; set; } = null!; + public string? Location { get; set; } + + // Navigation property + public EventDefinition? EventDefinition { get; set; } public bool SignupSubmitPickup => Name.Contains("Sign-up") || diff --git a/Data/AppDbContext.cs b/Data/AppDbContext.cs index f6ca913..bf52323 100644 --- a/Data/AppDbContext.cs +++ b/Data/AppDbContext.cs @@ -10,6 +10,7 @@ namespace Data public DbSet Students { get; set; } public DbSet Teams { get; set; } public DbSet StudentEventRanking { get; set; } + public DbSet EventOccurrences { get; set; } public AppDbContext() { diff --git a/Data/Configurations/EventOccurrenceConfiguration.cs b/Data/Configurations/EventOccurrenceConfiguration.cs new file mode 100644 index 0000000..ceaa5ad --- /dev/null +++ b/Data/Configurations/EventOccurrenceConfiguration.cs @@ -0,0 +1,48 @@ +using Core.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Data.Configurations +{ + public class EventOccurrenceConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(e => e.Id); + + // Indexes + builder.HasIndex(e => e.StartTime); + builder.HasIndex(e => e.EventDefinitionId); + builder.HasIndex(e => e.SpecialEventType); + + // Foreign key relationship (optional) + builder.HasOne(e => e.EventDefinition) + .WithMany() + .HasForeignKey(e => e.EventDefinitionId) + .OnDelete(DeleteBehavior.Restrict); // Don't cascade delete if EventDefinition is deleted + + // Constraints + builder.Property(e => e.Name) + .IsRequired() + .HasMaxLength(200); + + builder.Property(e => e.Time) + .IsRequired() + .HasMaxLength(100); + + builder.Property(e => e.Date) + .IsRequired() + .HasMaxLength(100); + + builder.Property(e => e.Location) + .HasMaxLength(500); + + builder.Property(e => e.SpecialEventType) + .HasMaxLength(50); + + // Validation: Either EventDefinitionId OR SpecialEventType must be set (enforced at application level) + // EF Core doesn't support mutually exclusive constraints directly, so we'll handle this in validation attributes or service layer + } + } +} + diff --git a/Data/Migrations/20251227205816_EventOccurrence.Designer.cs b/Data/Migrations/20251227205816_EventOccurrence.Designer.cs new file mode 100644 index 0000000..e382738 --- /dev/null +++ b/Data/Migrations/20251227205816_EventOccurrence.Designer.cs @@ -0,0 +1,339 @@ +// +using System; +using Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20251227205816_EventOccurrence")] + partial class EventOccurrence + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.8"); + + modelBuilder.Entity("Core.Entities.EventDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChapterEligibilityCountRegionals") + .HasColumnType("INTEGER"); + + b.Property("ChapterEligibilityCountState") + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("Documentation") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Eligibility") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("EventFormat") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LevelOfEffort") + .HasColumnType("INTEGER"); + + b.Property("MaxTeamSize") + .HasColumnType("INTEGER"); + + b.Property("MinTeamSize") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(1024) + .HasColumnType("TEXT"); + + b.Property("OnSiteActivity") + .HasColumnType("INTEGER"); + + b.Property("Presubmission") + .HasColumnType("INTEGER"); + + b.Property("SemifinalistActivity") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Theme") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventFormat"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Core.Entities.EventOccurrence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("EndTime") + .HasColumnType("TEXT"); + + b.Property("EventDefinitionId") + .HasColumnType("INTEGER"); + + b.Property("Location") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("SpecialEventType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("Time") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventDefinitionId"); + + b.HasIndex("SpecialEventType"); + + b.HasIndex("StartTime"); + + b.ToTable("EventOccurrences"); + }); + + modelBuilder.Entity("Core.Entities.Student", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Grade") + .HasColumnType("INTEGER"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("NationalId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OfficerRole") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("RegionalId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("StateId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("TsaYear") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("Grade"); + + b.HasIndex("FirstName", "LastName"); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("Core.Entities.StudentEventRanking", b => + { + b.Property("StudentId") + .HasColumnType("INTEGER"); + + b.Property("EventDefinitionId") + .HasColumnType("INTEGER"); + + b.Property("Rank") + .HasColumnType("INTEGER"); + + b.HasKey("StudentId", "EventDefinitionId"); + + b.HasIndex("EventDefinitionId"); + + b.HasIndex("Rank"); + + b.HasIndex("StudentId"); + + b.ToTable("StudentEventRanking"); + }); + + modelBuilder.Entity("Core.Entities.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CaptainId") + .HasColumnType("INTEGER"); + + b.Property("EventId") + .HasColumnType("INTEGER"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CaptainId"); + + b.HasIndex("EventId"); + + b.HasIndex("EventId", "Identifier"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("StudentTeam", b => + { + b.Property("StudentsId") + .HasColumnType("INTEGER"); + + b.Property("TeamsId") + .HasColumnType("INTEGER"); + + b.HasKey("StudentsId", "TeamsId"); + + b.HasIndex("TeamsId"); + + b.ToTable("TeamStudents", (string)null); + }); + + modelBuilder.Entity("Core.Entities.EventOccurrence", b => + { + b.HasOne("Core.Entities.EventDefinition", "EventDefinition") + .WithMany() + .HasForeignKey("EventDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EventDefinition"); + }); + + modelBuilder.Entity("Core.Entities.StudentEventRanking", b => + { + b.HasOne("Core.Entities.EventDefinition", "EventDefinition") + .WithMany() + .HasForeignKey("EventDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.Student", "Student") + .WithMany("EventRankings") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventDefinition"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Core.Entities.Team", b => + { + b.HasOne("Core.Entities.Student", "Captain") + .WithMany() + .HasForeignKey("CaptainId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Core.Entities.EventDefinition", "Event") + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Captain"); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("StudentTeam", b => + { + b.HasOne("Core.Entities.Student", null) + .WithMany() + .HasForeignKey("StudentsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.Team", null) + .WithMany() + .HasForeignKey("TeamsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Core.Entities.Student", b => + { + b.Navigation("EventRankings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20251227205816_EventOccurrence.cs b/Data/Migrations/20251227205816_EventOccurrence.cs new file mode 100644 index 0000000..5767f69 --- /dev/null +++ b/Data/Migrations/20251227205816_EventOccurrence.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Data.Migrations +{ + /// + public partial class EventOccurrence : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "EventOccurrences", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + EventDefinitionId = table.Column(type: "INTEGER", nullable: true), + SpecialEventType = table.Column(type: "TEXT", maxLength: 50, nullable: true), + Name = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Time = table.Column(type: "TEXT", maxLength: 100, nullable: false), + Date = table.Column(type: "TEXT", maxLength: 100, nullable: false), + StartTime = table.Column(type: "TEXT", nullable: false), + EndTime = table.Column(type: "TEXT", nullable: true), + Location = table.Column(type: "TEXT", maxLength: 500, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EventOccurrences", x => x.Id); + table.ForeignKey( + name: "FK_EventOccurrences_Events_EventDefinitionId", + column: x => x.EventDefinitionId, + principalTable: "Events", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_EventOccurrences_EventDefinitionId", + table: "EventOccurrences", + column: "EventDefinitionId"); + + migrationBuilder.CreateIndex( + name: "IX_EventOccurrences_SpecialEventType", + table: "EventOccurrences", + column: "SpecialEventType"); + + migrationBuilder.CreateIndex( + name: "IX_EventOccurrences_StartTime", + table: "EventOccurrences", + column: "StartTime"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EventOccurrences"); + } + } +} diff --git a/Data/Migrations/AppDbContextModelSnapshot.cs b/Data/Migrations/AppDbContextModelSnapshot.cs index e3c7d3b..9c3ecfb 100644 --- a/Data/Migrations/AppDbContextModelSnapshot.cs +++ b/Data/Migrations/AppDbContextModelSnapshot.cs @@ -1,4 +1,5 @@ // +using System; using Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -93,6 +94,55 @@ namespace Data.Migrations b.ToTable("Events"); }); + modelBuilder.Entity("Core.Entities.EventOccurrence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("EndTime") + .HasColumnType("TEXT"); + + b.Property("EventDefinitionId") + .HasColumnType("INTEGER"); + + b.Property("Location") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("SpecialEventType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("Time") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventDefinitionId"); + + b.HasIndex("SpecialEventType"); + + b.HasIndex("StartTime"); + + b.ToTable("EventOccurrences"); + }); + modelBuilder.Entity("Core.Entities.Student", b => { b.Property("Id") @@ -214,6 +264,16 @@ namespace Data.Migrations b.ToTable("TeamStudents", (string)null); }); + modelBuilder.Entity("Core.Entities.EventOccurrence", b => + { + b.HasOne("Core.Entities.EventDefinition", "EventDefinition") + .WithMany() + .HasForeignKey("EventDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("EventDefinition"); + }); + modelBuilder.Entity("Core.Entities.StudentEventRanking", b => { b.HasOne("Core.Entities.EventDefinition", "EventDefinition")