2using System.Globalization;
4using System.Reflection;
6using System.Threading.Tasks;
8using Microsoft.EntityFrameworkCore;
9using Microsoft.EntityFrameworkCore.Infrastructure;
10using Microsoft.EntityFrameworkCore.Migrations;
11using Microsoft.Extensions.DependencyInjection;
12using Microsoft.Extensions.Logging;
23#pragma warning disable CA1506
29 public DbSet<User>
Users {
get;
set; }
79 public DbSet<Job>
Jobs {
get;
set; }
114 public DbSet<UserGroup>
Groups {
get;
set; }
275 var configureFunction = typeof(TDatabaseContext).GetMethod(
277 BindingFlags.Public | BindingFlags.Static)
278 ??
throw new InvalidOperationException($
"Context type {typeof(TDatabaseContext).FullName} missing static {ConfigureMethodName} function!");
279 return (optionsBuilder, config) => configureFunction.Invoke(
null, [optionsBuilder, config]);
287 : base(dbContextOptions)
310 public Task
Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
313 public Task
Drop(CancellationToken cancellationToken) => Database.EnsureDeletedAsync(cancellationToken);
316 public async ValueTask<bool>
Migrate(ILogger<DatabaseContext> logger, CancellationToken cancellationToken)
318 ArgumentNullException.ThrowIfNull(logger);
319 var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken);
320 var wasEmpty = !migrations.Any();
322 if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken)).Any())
324 logger.LogInformation(
"Migrating database...");
325 await Database.MigrateAsync(cancellationToken);
328 logger.LogDebug(
"No migrations to apply");
330 wasEmpty |= !await
Users.AsQueryable().AnyAsync(cancellationToken);
337 ILogger<DatabaseContext> logger,
338 Version targetVersion,
340 CancellationToken cancellationToken)
342 ArgumentNullException.ThrowIfNull(logger);
343 ArgumentNullException.ThrowIfNull(targetVersion);
344 if (targetVersion <
new Version(4, 0))
345 throw new ArgumentOutOfRangeException(nameof(targetVersion), targetVersion,
"Cannot migrate below version 4.0.0!");
347 if (currentDatabaseType ==
DatabaseType.PostgresSql && targetVersion <
new Version(4, 3, 0))
348 throw new NotSupportedException(
"Cannot migrate below version 4.3.0 with PostgresSql!");
353 if (targetVersion <
new Version(4, 1, 0))
354 throw new NotSupportedException(
"Cannot migrate below version 4.1.0!");
358 if (targetMigration ==
null)
360 logger.LogDebug(
"No down migration required.");
365 var migrationSubstitution = currentDatabaseType
switch
367 DatabaseType.SqlServer =>
null,
368 DatabaseType.MySql =>
"MY{0}",
369 DatabaseType.Sqlite =>
"SL{0}",
370 DatabaseType.PostgresSql =>
"PG{0}",
371 _ =>
throw new InvalidOperationException($
"Invalid DatabaseType: {currentDatabaseType}"),
374 if (migrationSubstitution !=
null)
375 targetMigration = String.Format(CultureInfo.InvariantCulture, migrationSubstitution, targetMigration[2..]);
378 var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
379 var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
381 logger.LogInformation(
"Migrating down to version {targetVersion}. Target: {targetMigration}", targetVersion, targetMigration);
384 await migrator.MigrateAsync(targetMigration, cancellationToken);
388 logger.LogCritical(e,
"Failed to migrate!");
395 ArgumentNullException.ThrowIfNull(modelBuilder);
397 base.OnModelCreating(modelBuilder);
399 var userModel = modelBuilder.Entity<
User>();
400 userModel.HasIndex(x => x.CanonicalName).IsUnique();
401 userModel.HasIndex(x => x.SystemIdentifier).IsUnique();
402 userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
403 userModel.HasMany(x => x.OAuthConnections).WithOne(x => x.User).OnDelete(DeleteBehavior.Cascade);
404 userModel.HasMany(x => x.OidcConnections).WithOne(x => x.User).OnDelete(DeleteBehavior.Cascade);
406 modelBuilder.Entity<
OAuthConnection>().HasIndex(x =>
new { x.Provider, x.ExternalUserId }).IsUnique();
407 modelBuilder.Entity<
OidcConnection>().HasIndex(x =>
new { x.SchemeKey, x.ExternalUserId }).IsUnique();
409 var groupsModel = modelBuilder.Entity<
UserGroup>();
410 groupsModel.HasIndex(x => x.Name).IsUnique();
411 groupsModel.HasMany(x => x.Users).WithOne(x => x.Group).OnDelete(DeleteBehavior.ClientSetNull);
413 var permissionSetModel = modelBuilder.Entity<
PermissionSet>();
414 permissionSetModel.HasOne(x => x.Group).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
415 permissionSetModel.HasOne(x => x.User).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
416 permissionSetModel.HasMany(x => x.InstancePermissionSets).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
418 modelBuilder.Entity<
InstancePermissionSet>().HasIndex(x =>
new { x.PermissionSetId, x.InstanceId }).IsUnique();
421 revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
422 revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Cascade);
423 revInfo.HasIndex(x =>
new { x.InstanceId, x.CommitSha }).IsUnique();
435 modelBuilder.Entity<
TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.ClientNoAction);
437 var compileJob = modelBuilder.Entity<
CompileJob>();
438 compileJob.HasIndex(x => x.DirectoryName);
439 compileJob.HasOne(x => x.Job).WithOne().OnDelete(DeleteBehavior.Cascade);
441 modelBuilder.Entity<
ReattachInformation>().HasOne(x => x.CompileJob).WithMany().OnDelete(DeleteBehavior.Cascade);
443 var chatChannel = modelBuilder.Entity<
ChatChannel>();
444 chatChannel.HasIndex(x =>
new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
445 chatChannel.HasIndex(x =>
new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
446 chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
448 modelBuilder.Entity<
ChatBot>().HasIndex(x =>
new { x.InstanceId, x.Name }).IsUnique();
450 var instanceModel = modelBuilder.Entity<
Instance>();
451 instanceModel.HasIndex(x =>
new { x.Path, x.SwarmIdentifer }).IsUnique();
452 instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
453 instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
454 instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
455 instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
456 instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
457 instanceModel.HasMany(x => x.InstancePermissionSets).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
458 instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
496 string? targetMigration =
null;
498 string BadDatabaseType() =>
throw new ArgumentException($
"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
501 if (targetVersion <
new Version(6, 15, 0))
502 targetMigration = currentDatabaseType
switch
508 _ => BadDatabaseType(),
511 if (targetVersion <
new Version(6, 12, 0))
512 targetMigration = currentDatabaseType
switch
518 _ => BadDatabaseType(),
521 if (targetVersion <
new Version(6, 7, 0))
522 targetMigration = currentDatabaseType
switch
528 _ => BadDatabaseType(),
531 if (targetVersion <
new Version(6, 6, 0))
532 targetMigration = currentDatabaseType
switch
538 _ => BadDatabaseType(),
541 if (targetVersion <
new Version(6, 5, 0))
542 targetMigration = currentDatabaseType
switch
548 _ => BadDatabaseType(),
551 if (targetVersion <
new Version(6, 2, 0))
552 targetMigration = currentDatabaseType
switch
558 _ => BadDatabaseType(),
561 if (targetVersion <
new Version(6, 0, 0))
562 targetMigration = currentDatabaseType
switch
568 _ => BadDatabaseType(),
570 if (targetVersion <
new Version(5, 17, 0))
571 targetMigration = currentDatabaseType
switch
577 _ => BadDatabaseType(),
579 if (targetVersion <
new Version(5, 13, 0))
580 targetMigration = currentDatabaseType
switch
586 _ => BadDatabaseType(),
588 if (targetVersion <
new Version(5, 7, 3))
589 targetMigration = currentDatabaseType
switch
595 _ => BadDatabaseType(),
597 if (targetVersion <
new Version(5, 7, 0))
598 targetMigration = currentDatabaseType
switch
604 _ => BadDatabaseType(),
606 if (targetVersion <
new Version(4, 19, 0))
607 targetMigration = currentDatabaseType
switch
613 _ => BadDatabaseType(),
615 if (targetVersion <
new Version(4, 18, 0))
616 targetMigration = currentDatabaseType
switch
622 _ => BadDatabaseType(),
624 if (targetVersion <
new Version(4, 14, 0))
625 targetMigration = currentDatabaseType
switch
631 _ => BadDatabaseType(),
633 if (targetVersion <
new Version(4, 10, 0))
634 targetMigration = currentDatabaseType
switch
640 _ => BadDatabaseType(),
642 if (targetVersion <
new Version(4, 8, 0))
643 targetMigration = currentDatabaseType
switch
649 _ => BadDatabaseType(),
651 if (targetVersion <
new Version(4, 7, 0))
652 targetMigration = currentDatabaseType
switch
658 _ => BadDatabaseType(),
660 if (targetVersion <
new Version(4, 6, 0))
661 targetMigration = currentDatabaseType
switch
667 _ => BadDatabaseType(),
669 if (targetVersion <
new Version(4, 5, 0))
670 targetMigration = currentDatabaseType
switch
676 _ => BadDatabaseType(),
678 if (targetVersion <
new Version(4, 4, 0))
679 targetMigration = currentDatabaseType
switch
682 DatabaseType.PostgresSql => nameof(
PGCreate),
685 _ => BadDatabaseType(),
688 if (targetVersion <
new Version(4, 2, 0))
691 return targetMigration;
Backend abstract implementation of IDatabaseContext.
DbSet< ReattachInformation > ReattachInformations
The ReattachInformations in the DatabaseContext.
readonly IDatabaseCollection< UserGroup > groups
Backing field for IDatabaseContext.Groups.
DbSet< Job > Jobs
The Jobs in the DatabaseContext.
DbSet< Instance > Instances
The Instances in the DatabaseContext.
readonly IDatabaseCollection< RevInfoTestMerge > revInfoTestMergesCollection
Backing field for IDatabaseContext.RevInfoTestMerges.
readonly IDatabaseCollection< DreamMakerSettings > dreamMakerSettingsCollection
Backing field for IDatabaseContext.DreamMakerSettings.
virtual DeleteBehavior RevInfoCompileJobDeleteBehavior
The DeleteBehavior for the CompileJob/RevisionInformation foreign key.
readonly IDatabaseCollection< OidcConnection > oidcConnections
Backing field for IDatabaseContext.OidcConnections.
readonly IDatabaseCollection< RepositorySettings > repositorySettingsCollection
Backing field for IDatabaseContext.RepositorySettings.
DbSet< OAuthConnection > OAuthConnections
The OAuthConnections in the DatabaseContext.
readonly IDatabaseCollection< User > usersCollection
Backing field for IDatabaseContext.Users.
readonly IDatabaseCollection< Job > jobsCollection
Backing field for IDatabaseContext.Jobs.
override void OnModelCreating(ModelBuilder modelBuilder)
DbSet< ChatChannel > ChatChannels
The ChatChannels in the DatabaseContext.
DatabaseContext(DbContextOptions dbContextOptions)
Initializes a new instance of the DatabaseContext class.
readonly IDatabaseCollection< DreamDaemonSettings > dreamDaemonSettingsCollection
Backing field for IDatabaseContext.DreamDaemonSettings.
readonly IDatabaseCollection< Instance > instancesCollection
Backing field for IDatabaseContext.Instances.
DbSet< InstancePermissionSet > InstancePermissionSets
The InstancePermissionSets in the DatabaseContext.
DbSet< PermissionSet > PermissionSets
The PermissionSets in the DatabaseContext.
readonly IDatabaseCollection< ChatChannel > chatChannelsCollection
Backing field for IDatabaseContext.ChatChannels.
DbSet< CompileJob > CompileJobs
The CompileJobs in the DatabaseContext.
async ValueTask< bool > Migrate(ILogger< DatabaseContext > logger, CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext.A ValueTask<TResult> resulting in true if the database shou...
readonly IDatabaseCollection< RevisionInformation > revisionInformationsCollection
Backing field for IDatabaseContext.RevisionInformations.
DbSet< TestMerge > TestMerges
The TestMerges in the DatabaseContext.
async ValueTask SchemaDowngradeForServerVersion(ILogger< DatabaseContext > logger, Version targetVersion, DatabaseType currentDatabaseType, CancellationToken cancellationToken)
Attempt to downgrade the schema to the migration used for a given server targetVersion ....
readonly IDatabaseCollection< InstancePermissionSet > instancePermissionSetsCollection
Backing field for IDatabaseContext.InstancePermissionSets.
Task Drop(CancellationToken cancellationToken)
Attempts to delete all tables and drop the database in use.A Task representing the running operation.
readonly IDatabaseCollection< ReattachInformation > reattachInformationsCollection
Backing field for IDatabaseContext.ReattachInformations.
Task Save(CancellationToken cancellationToken)
Saves changes made to the IDatabaseContext.A Task representing the running operation.
readonly IDatabaseCollection< PermissionSet > permissionSets
Backing field for IDatabaseContext.PermissionSets.
readonly IDatabaseCollection< ChatBot > chatBotsCollection
Backing field for IDatabaseContext.ChatBots.
readonly IDatabaseCollection< CompileJob > compileJobsCollection
Backing field for IDatabaseContext.CompileJobs.
string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
Gets the name of the migration to run for migrating down to a given targetVersion for the currentDat...
DbSet< User > Users
The Users in the DatabaseContext.
DbSet< ChatBot > ChatBots
The ChatBots in the DatabaseContext.
DbSet< OidcConnection > OidcConnections
The OidcConnections in the DatabaseContext.
DbSet< RevInfoTestMerge > RevInfoTestMerges
The RevInfoTestMerges in the DatabaseContext.
readonly IDatabaseCollection< OAuthConnection > oAuthConnections
Backing field for IDatabaseContext.OAuthConnections.
readonly IDatabaseCollection< TestMerge > testMergesCollection
Backing field for IDatabaseContext.TestMerges.
static Action< DbContextOptionsBuilder, DatabaseConfiguration > GetConfigureAction< TDatabaseContext >()
Gets the configure action for a given TDatabaseContext .
DbSet< RevisionInformation > RevisionInformations
The RevisionInformations in the DatabaseContext.
DbSet< UserGroup > Groups
The UserGroups in the DatabaseContext.
Adds the AdditionalParameters DD column for MSSQL.
Adds columns for GitHub deployments for MSSQL.
Adds the DreamDaemon LogOutput column for MSSQL.
Adds the DreamMakerSettings DumpOnHeartbeatRestart column for MSSQL.
Adds the MapThreads DreamDaemonSettings column for MSSQL.
Adds the option to start the profiler with DreamDaemon for MSSQL.
Adds the InitialCompileJobId to the ReattachInformations table for MSSQL.
Add the Timestamp column to RevisionInformations for MSSQL.
Adds the swarm identifier column for MSSQL.
Adds the UpdateSubmodules repository setting for MSSQL.
Update models for making the DMAPI optional for MSSQL.
Fix cascading data deletes for Models.Instances on MSSQL.
Removes various defunct columns for MSSQL.
Reduces the index name column size for MSSQL.
Adds the AdditionalParameters DD column for MySQL.
Adds columns for GitHub deployments for MYSQL.
Adds the DreamDaemon LogOutput column for MYSQL.
Adds the DreamMakerSettings DumpOnHeartbeatRestart column for MYSQL.
Adds the MapThreads DreamDaemonSettings column for MYSQL.
Adds the option to start the profiler with DreamDaemon for MYSQL.
Adds the InitialCompileJobId to the ReattachInformations table for MYSQL.
Adds the swarm identifier column for MySQL.
Adds the UpdateSubmodules repository setting for MYSQL.
Update models for making the DMAPI optional for MYSQL.
Fix the CompileJob/RevisionInformation foreign key for MySQL.
Reduces the index name column size for MYSQL.
Adds the AdditionalParameters DD column for PostgresSQL.
Adds columns for GitHub deployments for PostgresSQL.
Adds the DreamDaemon LogOutput column for PostgresSQL.
Adds the DreamMakerSettings DumpOnHeartbeatRestart column for PostgresSQL.
Adds the MapThreads DreamDaemonSettings column for PostgresSQL.
Adds the option to start the profiler with DreamDaemon for PostgresSQL.
Adds the InitialCompileJobId to the ReattachInformations table for PostgresSQL.
Add the Timestamp column to RevisionInformations for PostgresSQL.
Adds the swarm identifier column for PostgresSQL.
Adds the UpdateSubmodules repository setting for PostgresSQL.
Update models for making the DMAPI optional for PostgresSQL.
Create initial schema for PostgresSQL.
Reduces the index name column size for PostgresSQL.
Adds columns for GitHub deployments for SQLite.
Adds columns for GitHub deployments for SQLite.
Adds the DreamDaemon LogOutput column for SQLite.
Adds the DreamMakerSettings DumpOnHeartbeatRestart column for SQLite.
Adds the MapThreads DreamDaemonSettings column for SQLite.
Adds the option to start the profiler with DreamDaemon for SQLite.
Adds the InitialCompileJobId to the ReattachInformations table for SQLite.
Add the Timestamp column to RevisionInformations for SQLite.
Adds the swarm identifier column for SQLite.
Adds the UpdateSubmodules repository setting for SQLite.
Update models for making the DMAPI optional for SQLite.
Rebuild of the schema for SQLite.
Removes various defunct columns for SQLite.
DatabaseContext for Sqlserver.
static void ConfigureWith(DbContextOptionsBuilder options, DatabaseConfiguration databaseConfiguration)
Configure the SqlServerDatabaseContext.
Represents an Api.Models.Instance in the database.
Represents a group of Users.
Represents a database table.
DatabaseType
Type of database to user.