using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data; using System.Linq; namespace Data.SeaHavenIndustries { public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); foreach (var foreignKey in builder.Model.GetEntityTypes().SelectMany (e => e.GetForeignKeys())) { foreignKey.DeleteBehavior = DeleteBehavior.Restrict; } builder.Entity() .HasIndex(t => t.Token) .IsUnique(); builder.Entity() .HasIndex(u => u.DispatchId) .IsUnique() .HasFilter("[Status] IN ('Pending', 'ChangesRequested')"); builder.Entity() .HasIndex(u => new { u.DispatchId, u.RequestKey }) .IsUnique() .HasFilter("[RequestKey] IS NOT NULL"); builder.Entity() .Property(u => u.RowVersion) .IsRowVersion(); builder.Entity() .HasIndex(l => new { l.WorkOrderId, l.FieldName }) .IsUnique(); builder.Entity() .HasIndex(l => new { l.WorkOrderId, l.SourceWeekStart }) .IsUnique(); builder.Entity() .HasIndex(t => new { t.ServiceKey, t.IsActive }); builder.Entity() .Property(w => w.RowVersion) .IsRowVersion(); if (Database.IsSqlServer()) builder.HasSequence("WorkOrderInternalNumberSequence"); // Bounded lengths required for SQL Server index keys (nvarchar(max) is not indexable). // SiteCode: board create contract MaximumLength(32). InternalWONumber: normalized to 11 digits. builder.Entity() .Property(w => w.SiteCode) .HasMaxLength(32); builder.Entity() .Property(w => w.InternalWONumber) .HasMaxLength(11); builder.Entity() .HasIndex(w => w.SiteCode) .HasFilter("[istemplate] = 0"); builder.Entity() .HasIndex(w => w.InternalWONumber) .HasFilter("[istemplate] = 0"); builder.Entity() .HasIndex(w => new { w.LifecycleStatus, w.ScheduledDate }) .HasFilter("[istemplate] = 0"); // These indexes already exist in the migration history. Keep them in // the runtime model so future additive migrations do not drop them. builder.Entity() .HasIndex(w => new { w.AssignTo, w.ScheduledDate }); builder.Entity() .HasIndex(w => w.ScheduledDate); builder.Entity() .HasIndex(w => w.WorkOrderType); builder.Entity() .Property(w => w.ExternalWorkOrderId) .HasMaxLength(450); builder.Entity() .HasIndex(w => w.ExternalWorkOrderId) .IsUnique() .HasFilter("[ExternalWorkOrderId] IS NOT NULL AND [ExternalWorkOrderId] <> ''"); builder.Entity() .HasIndex(d => d.DeliveryId) .IsUnique(); builder.Entity() .Property(c => c.ExternalSource) .HasMaxLength(128); builder.Entity() .Property(c => c.ExternalVersionHash) .HasMaxLength(64); builder.Entity() .Property(c => c.ExternalSourceEmailS3Key) .HasMaxLength(2048); builder.Entity() .Property(l => l.ExternalSource) .HasMaxLength(128); builder.Entity() .Property(l => l.ExternalLocationId) .HasMaxLength(450); builder.Entity() .HasOne(l => l.Account) .WithMany() .HasForeignKey(l => l.AccountId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasIndex(l => l.AccountId); builder.Entity() .HasIndex(r => new { r.Source, r.Kind, r.ExternalId }) .IsUnique() .HasDatabaseName("IX_WorkOrderExternalReceipts_Source_Kind_ExternalId"); builder.Entity() .HasIndex(j => j.State); builder.Entity() .Property(j => j.Id) .ValueGeneratedNever(); builder.Entity() .Property(d => d.RowVersion) .IsRowVersion(); builder.Entity() .HasOne(w => w.PrimaryDispatch) .WithMany() .HasForeignKey(w => w.PrimaryDispatchId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasOne(w => w.Account) .WithMany() .HasForeignKey(w => w.AccountId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasIndex(w => w.AccountId); builder.Entity() .HasOne(u => u.Account) .WithMany() .HasForeignKey(u => u.AccountId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasIndex(u => u.AccountId); builder.Entity() .HasIndex(c => c.NormalizedName) .IsUnique(); builder.Entity() .Property(c => c.RowVersion) .IsRowVersion(); builder.Entity() .HasOne(v => v.Company) .WithMany(c => c.Vendors) .HasForeignKey(v => v.CompanyId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasIndex(p => new { p.LocationId, p.VendorId, p.Trade }) .IsUnique() .HasFilter("[Trade] IS NOT NULL"); builder.Entity() .HasIndex(p => new { p.LocationId, p.VendorId }) .IsUnique() .HasFilter("[Trade] IS NULL"); // SH-249 bounded lengths: unique index keys must not be nvarchar(max). builder.Entity() .Property(t => t.Name) .HasMaxLength(128); builder.Entity() .Property(t => t.NormalizedName) .HasMaxLength(128); builder.Entity() .HasIndex(t => t.NormalizedName) .IsUnique(); // SH-278 Area catalogue: bounded names so the unique key is indexable. builder.Entity() .Property(a => a.Name) .HasMaxLength(128); builder.Entity() .Property(a => a.NormalizedName) .HasMaxLength(128); builder.Entity() .HasIndex(a => a.NormalizedName) .IsUnique(); builder.Entity() .HasOne(c => c.Area) .WithMany() .HasForeignKey(c => c.AreaId) .OnDelete(DeleteBehavior.Restrict); // Per-person team permission overrides. Composite primary key // (UserId + PermissionKey) plus an explicitly named unique composite // index; missing rows behave as Unset. builder.Entity(entity => { entity.HasKey(o => new { o.UserId, o.PermissionKey }); entity.Property(o => o.UserId) .HasMaxLength(450); entity.Property(o => o.PermissionKey) .HasMaxLength(64); entity.Property(o => o.State) .IsRequired(); entity.HasOne(o => o.User) .WithMany() .HasForeignKey(o => o.UserId) .OnDelete(DeleteBehavior.Restrict); entity.HasIndex(o => new { o.UserId, o.PermissionKey }) .IsUnique() .HasDatabaseName("IX_UserPermissionOverrides_UserId_PermissionKey"); }); } public DbSet Categories { get; set; } public DbSet Locations { get; set; } public DbSet Contacts { get; set; } public DbSet workOrders { get; set; } public DbSet