using Microsoft.EntityFrameworkCore; using Microsoft.SelfService.Portal.Core.API.Extensions.Dataannotations; using Microsoft.SelfService.Portal.Core.API.Models; using System.Reflection; namespace Microsoft.SelfService.Portal.Core.API.Context { public class DataContext : DbContext { public DataContext(DbContextOptions options) : base(options) { } public DbSet Environments { get; set; } public DbSet EnvironmentDomains { get; set; } public DbSet Domains { get; set; } public DbSet Targets { get; set; } public DbSet Deployments { get; set; } public DbSet DeploymentGroups { get; set; } public DbSet Templates { get; set; } public DbSet TemplateVersions { get; set; } public DbSet DeploymentRules { get; set; } public DbSet DeploymentRuleSteps { get; set; } public DbSet DeploymentTemplateSelections { get; set; } public DbSet DeploymentParameterValues { get; set; } public DbSet DeploymentTargetAssignments { get; set; } public DbSet TemplateCategories { get; set; } public DbSet Services { get; set; } public DbSet ServiceRoleDefinitions { get; set; } public DbSet TemplateOptions { get; set; } public DbSet Options { get; set; } public DbSet OptionCategories { get; set; } public DbSet QueueJobs { get; set; } public DbSet QueueJobTargets { get; set; } public DbSet QueueJobSteps { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity() .HasKey(ed => new { ed.EnvironmentId, ed.DomainId }); modelBuilder.Entity() .HasOne(ed => ed.Environment) .WithMany(e => e.EnvironmentDomains) .HasForeignKey(ed => ed.EnvironmentId); modelBuilder.Entity() .HasOne(ed => ed.Domain) .WithMany(d => d.EnvironmentDomains) .HasForeignKey(ed => ed.DomainId); modelBuilder.Entity() .HasKey(to => new { to.OptionId, to.TemplateId }); modelBuilder.Entity() .HasOne(to => to.Option) .WithMany(o => o.TemplateOptions) .HasForeignKey(to => to.OptionId); modelBuilder.Entity() .HasOne(to => to.Template) .WithMany(t => t.TemplateOptions) .HasForeignKey(to => to.TemplateId); modelBuilder.Entity() .HasOne(tc => tc.Service) .WithMany(s => s.TemplateCategories) .HasForeignKey(tc => tc.ServiceId); modelBuilder.Entity() .HasOne(role => role.Service) .WithMany(service => service.RoleDefinitions) .HasForeignKey(role => role.ServiceId); modelBuilder.Entity() .ToTable("DeploymentBatches"); modelBuilder.Entity() .Property(target => target.TargetType) .HasMaxLength(64); modelBuilder.Entity() .Property(target => target.ProviderType) .HasMaxLength(64); modelBuilder.Entity() .ToTable("DeploymentExecutions"); modelBuilder.Entity() .Property(deployment => deployment.DeploymentGroupId) .HasColumnName("DeploymentBatchId"); modelBuilder.Entity() .ToTable("DeploymentJobs"); modelBuilder.Entity() .HasIndex(job => new { job.Status, job.ScheduledAt, job.LockedUntil, job.Priority, job.Created }); modelBuilder.Entity() .HasIndex(job => job.CorrelationId); modelBuilder.Entity() .HasIndex(job => job.WorkerName); modelBuilder.Entity() .Property(job => job.CorrelationId) .HasDefaultValueSql("NEWID()"); modelBuilder.Entity() .Property(job => job.Priority) .HasDefaultValue(100); modelBuilder.Entity() .Property(job => job.RowVersion) .IsRowVersion(); modelBuilder.Entity() .ToTable("DeploymentJobTargets"); modelBuilder.Entity() .Property(target => target.QueueJobId) .HasColumnName("DeploymentJobId"); modelBuilder.Entity() .Property(target => target.DeploymentGroupId) .HasColumnName("DeploymentBatchId"); modelBuilder.Entity() .HasIndex(target => new { target.QueueJobId, target.Status }); modelBuilder.Entity() .ToTable(table => table.HasCheckConstraint("CK_DeploymentJobTargets_OutputMetadataJson_IsJson", "[OutputMetadataJson] IS NULL OR ISJSON([OutputMetadataJson]) = 1")); modelBuilder.Entity() .ToTable("DeploymentJobSteps"); modelBuilder.Entity() .Property(step => step.QueueJobId) .HasColumnName("DeploymentJobId"); modelBuilder.Entity() .Property(step => step.DependsOnQueueJobStepId) .HasColumnName("DependsOnDeploymentJobStepId"); modelBuilder.Entity() .HasIndex(step => new { step.QueueJobId, step.Status, step.SortOrder }); modelBuilder.Entity() .ToTable(table => table.HasCheckConstraint("CK_DeploymentJobSteps_OutputMetadataJson_IsJson", "[OutputMetadataJson] IS NULL OR ISJSON([OutputMetadataJson]) = 1")); modelBuilder.Entity() .HasKey(d => new { d.TargetId, d.DeploymentGroupId }); modelBuilder.Entity() .HasOne(d => d.Target) .WithMany(vm => vm.Deployments) .HasForeignKey(d => d.TargetId); modelBuilder.Entity() .HasOne(d => d.DeploymentGroup) .WithMany(dg => dg.Deployments) .HasForeignKey(d => d.DeploymentGroupId); modelBuilder.Entity() .HasOne(dg => dg.Template) .WithMany(t => t.DeploymentGroups) .HasForeignKey(dg => dg.TemplateId); modelBuilder.Entity() .HasOne(dg => dg.DeploymentRule) .WithMany() .HasForeignKey(dg => dg.DeploymentRuleId) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() .HasOne(selection => selection.DeploymentGroup) .WithMany(batch => batch.TemplateSelections) .HasForeignKey(selection => selection.DeploymentGroupId); modelBuilder.Entity() .HasOne(selection => selection.TemplateVersion) .WithMany() .HasForeignKey(selection => selection.TemplateVersionId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasIndex(selection => new { selection.DeploymentGroupId, selection.SortOrder }) .IsUnique(); modelBuilder.Entity() .HasOne(parameterValue => parameterValue.DeploymentGroup) .WithMany(batch => batch.ParameterValues) .HasForeignKey(parameterValue => parameterValue.DeploymentGroupId); modelBuilder.Entity() .HasOne(parameterValue => parameterValue.DeploymentTemplateSelection) .WithMany() .HasForeignKey(parameterValue => parameterValue.DeploymentTemplateSelectionId) .OnDelete(DeleteBehavior.NoAction); modelBuilder.Entity() .HasIndex(parameterValue => new { parameterValue.DeploymentGroupId, parameterValue.DeploymentTemplateSelectionId, parameterValue.Name }) .IsUnique() .HasFilter("[DeploymentTemplateSelectionId] IS NOT NULL"); modelBuilder.Entity() .HasIndex(parameterValue => new { parameterValue.DeploymentGroupId, parameterValue.Name }) .IsUnique() .HasFilter("[DeploymentTemplateSelectionId] IS NULL"); modelBuilder.Entity() .ToTable(table => table.HasCheckConstraint("CK_DeploymentParameterValues_ValueJson_IsJson", "ISJSON([ValueJson]) = 1")); modelBuilder.Entity() .HasOne(target => target.DeploymentGroup) .WithMany(batch => batch.TargetAssignments) .HasForeignKey(target => target.DeploymentGroupId); modelBuilder.Entity() .HasOne(target => target.Target) .WithMany() .HasForeignKey(target => target.TargetId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasIndex(target => new { target.DeploymentGroupId, target.TargetId, target.RoleKey }) .IsUnique(); modelBuilder.Entity() .ToTable(table => table.HasCheckConstraint("CK_DeploymentTargetAssignments_NodeDataJson_IsJson", "ISJSON([NodeDataJson]) = 1")); modelBuilder.Entity() .HasOne(template => template.DeploymentRule) .WithMany() .HasForeignKey(template => template.DeploymentRuleId); modelBuilder.Entity() .HasOne(version => version.Template) .WithMany(template => template.TemplateVersions) .HasForeignKey(version => version.TemplateId); modelBuilder.Entity() .HasIndex(version => new { version.TemplateId, version.Version }) .IsUnique(); modelBuilder.Entity() .HasIndex(version => version.TemplateId) .IsUnique() .HasFilter("[IsPublished] = 1"); modelBuilder.Entity() .Property(version => version.Version) .HasMaxLength(64); modelBuilder.Entity() .Property(version => version.JsonHash) .HasMaxLength(64); modelBuilder.Entity() .Property(version => version.SchemaVersion) .HasMaxLength(32); modelBuilder.Entity() .ToTable(table => table.HasCheckConstraint("CK_TemplateVersions_JsonData_IsJson", "ISJSON([JsonData]) = 1")); modelBuilder.Entity() .HasOne(step => step.DeploymentRule) .WithMany(rule => rule.Steps) .HasForeignKey(step => step.DeploymentRuleId); modelBuilder.Entity() .HasOne(target => target.QueueJob) .WithMany(job => job.Targets) .HasForeignKey(target => target.QueueJobId); modelBuilder.Entity() .HasOne(step => step.QueueJob) .WithMany(job => job.Steps) .HasForeignKey(step => step.QueueJobId); modelBuilder.Entity() .HasOne(step => step.DependsOnQueueJobStep) .WithMany() .HasForeignKey(step => step.DependsOnQueueJobStepId) .OnDelete(DeleteBehavior.Restrict); OnModelCreatingAddDefaultSqlValues(modelBuilder); DemoData.Seed(modelBuilder); } private void OnModelCreatingAddDefaultSqlValues(ModelBuilder modelBuilder) { var assemblyName = "Microsoft.SelfService.Portal.Core.API"; var nameSpace = "Microsoft.SelfService.Portal.Core.API.Models"; var asm = Assembly.Load(assemblyName); List types = asm.GetTypes().Where(p => p.Namespace == nameSpace).ToList(); var dbSets = typeof(DataContext).GetProperties().Where(p => p.PropertyType.Name.ToLower().Contains("dbset")).ToList(); List dbSetTypes = new List(); foreach (PropertyInfo pi in dbSets) { dbSetTypes.Add(pi.PropertyType.GetGenericArguments()[0]); } foreach (Type t in types) { if (typeof(BaseModel).IsAssignableFrom(t) && t.Name != nameof(BaseModel) && dbSetTypes.Contains(t)) { var properties = t.GetProperties().ToList(); foreach (var p in properties) { var att = p.GetCustomAttribute(); if (att != null) { modelBuilder.Entity(t).Property(p.Name).HasDefaultValueSql(att.DefaultValueSql); } } } } } } }