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; }
109 public DbSet<UserGroup>
Groups {
get;
set; }
262 var configureFunction = typeof(TDatabaseContext).GetMethod(
264 BindingFlags.Public | BindingFlags.Static)
265 ??
throw new InvalidOperationException($
"Context type {typeof(TDatabaseContext).FullName} missing static {ConfigureMethodName} function!");
266 return (optionsBuilder, config) => configureFunction.Invoke(
null, [optionsBuilder, config]);
274 : base(dbContextOptions)
296 public Task
Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
299 public Task
Drop(CancellationToken cancellationToken) => Database.EnsureDeletedAsync(cancellationToken);
302 public async ValueTask<bool>
Migrate(ILogger<DatabaseContext> logger, CancellationToken cancellationToken)
304 ArgumentNullException.ThrowIfNull(logger);
305 var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken);
306 var wasEmpty = !migrations.Any();
308 if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken)).Any())
310 logger.LogInformation(
"Migrating database...");
311 await Database.MigrateAsync(cancellationToken);
314 logger.LogDebug(
"No migrations to apply");
316 wasEmpty |= !await
Users.AsQueryable().AnyAsync(cancellationToken);
323 ILogger<DatabaseContext> logger,
324 Version targetVersion,
326 CancellationToken cancellationToken)
328 ArgumentNullException.ThrowIfNull(logger);
329 ArgumentNullException.ThrowIfNull(targetVersion);
330 if (targetVersion <
new Version(4, 0))
331 throw new ArgumentOutOfRangeException(nameof(targetVersion), targetVersion,
"Cannot migrate below version 4.0.0!");
333 if (currentDatabaseType ==
DatabaseType.PostgresSql && targetVersion <
new Version(4, 3, 0))
334 throw new NotSupportedException(
"Cannot migrate below version 4.3.0 with PostgresSql!");
339 if (targetVersion <
new Version(4, 1, 0))
340 throw new NotSupportedException(
"Cannot migrate below version 4.1.0!");
344 if (targetMigration ==
null)
346 logger.LogDebug(
"No down migration required.");
351 var migrationSubstitution = currentDatabaseType
switch
353 DatabaseType.SqlServer =>
null,
354 DatabaseType.MySql =>
"MY{0}",
355 DatabaseType.Sqlite =>
"SL{0}",
356 DatabaseType.PostgresSql =>
"PG{0}",
357 _ =>
throw new InvalidOperationException($
"Invalid DatabaseType: {currentDatabaseType}"),
360 if (migrationSubstitution !=
null)
361 targetMigration = String.Format(CultureInfo.InvariantCulture, migrationSubstitution, targetMigration[2..]);
364 var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
365 var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
367 logger.LogInformation(
"Migrating down to version {targetVersion}. Target: {targetMigration}", targetVersion, targetMigration);
370 await migrator.MigrateAsync(targetMigration, cancellationToken);
374 logger.LogCritical(e,
"Failed to migrate!");
381 ArgumentNullException.ThrowIfNull(modelBuilder);
383 base.OnModelCreating(modelBuilder);
385 var userModel = modelBuilder.Entity<
User>();
386 userModel.HasIndex(x => x.CanonicalName).IsUnique();
387 userModel.HasIndex(x => x.SystemIdentifier).IsUnique();
388 userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
389 userModel.HasMany(x => x.OAuthConnections).WithOne(x => x.User).OnDelete(DeleteBehavior.Cascade);
391 modelBuilder.Entity<
OAuthConnection>().HasIndex(x =>
new { x.Provider, x.ExternalUserId }).IsUnique();
393 var groupsModel = modelBuilder.Entity<
UserGroup>();
394 groupsModel.HasIndex(x => x.Name).IsUnique();
395 groupsModel.HasMany(x => x.Users).WithOne(x => x.Group).OnDelete(DeleteBehavior.ClientSetNull);
397 var permissionSetModel = modelBuilder.Entity<
PermissionSet>();
398 permissionSetModel.HasOne(x => x.Group).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
399 permissionSetModel.HasOne(x => x.User).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
400 permissionSetModel.HasMany(x => x.InstancePermissionSets).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
402 modelBuilder.Entity<
InstancePermissionSet>().HasIndex(x =>
new { x.PermissionSetId, x.InstanceId }).IsUnique();
405 revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
406 revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Cascade);
407 revInfo.HasIndex(x =>
new { x.InstanceId, x.CommitSha }).IsUnique();
419 modelBuilder.Entity<
TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.ClientNoAction);
421 var compileJob = modelBuilder.Entity<
CompileJob>();
422 compileJob.HasIndex(x => x.DirectoryName);
423 compileJob.HasOne(x => x.Job).WithOne().OnDelete(DeleteBehavior.Cascade);
425 modelBuilder.Entity<
ReattachInformation>().HasOne(x => x.CompileJob).WithMany().OnDelete(DeleteBehavior.Cascade);
427 var chatChannel = modelBuilder.Entity<
ChatChannel>();
428 chatChannel.HasIndex(x =>
new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
429 chatChannel.HasIndex(x =>
new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
430 chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
432 modelBuilder.Entity<
ChatBot>().HasIndex(x =>
new { x.InstanceId, x.Name }).IsUnique();
434 var instanceModel = modelBuilder.Entity<
Instance>();
435 instanceModel.HasIndex(x =>
new { x.Path, x.SwarmIdentifer }).IsUnique();
436 instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
437 instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
438 instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
439 instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
440 instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
441 instanceModel.HasMany(x => x.InstancePermissionSets).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
442 instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
480 string? targetMigration =
null;
482 string BadDatabaseType() =>
throw new ArgumentException($
"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
485 if (targetVersion <
new Version(6, 12, 0))
486 targetMigration = currentDatabaseType
switch
492 _ => BadDatabaseType(),
495 if (targetVersion <
new Version(6, 7, 0))
496 targetMigration = currentDatabaseType
switch
502 _ => BadDatabaseType(),
505 if (targetVersion <
new Version(6, 6, 0))
506 targetMigration = currentDatabaseType
switch
512 _ => BadDatabaseType(),
515 if (targetVersion <
new Version(6, 5, 0))
516 targetMigration = currentDatabaseType
switch
522 _ => BadDatabaseType(),
525 if (targetVersion <
new Version(6, 2, 0))
526 targetMigration = currentDatabaseType
switch
532 _ => BadDatabaseType(),
535 if (targetVersion <
new Version(6, 0, 0))
536 targetMigration = currentDatabaseType
switch
542 _ => BadDatabaseType(),
544 if (targetVersion <
new Version(5, 17, 0))
545 targetMigration = currentDatabaseType
switch
551 _ => BadDatabaseType(),
553 if (targetVersion <
new Version(5, 13, 0))
554 targetMigration = currentDatabaseType
switch
560 _ => BadDatabaseType(),
562 if (targetVersion <
new Version(5, 7, 3))
563 targetMigration = currentDatabaseType
switch
569 _ => BadDatabaseType(),
571 if (targetVersion <
new Version(5, 7, 0))
572 targetMigration = currentDatabaseType
switch
578 _ => BadDatabaseType(),
580 if (targetVersion <
new Version(4, 19, 0))
581 targetMigration = currentDatabaseType
switch
587 _ => BadDatabaseType(),
589 if (targetVersion <
new Version(4, 18, 0))
590 targetMigration = currentDatabaseType
switch
596 _ => BadDatabaseType(),
598 if (targetVersion <
new Version(4, 14, 0))
599 targetMigration = currentDatabaseType
switch
605 _ => BadDatabaseType(),
607 if (targetVersion <
new Version(4, 10, 0))
608 targetMigration = currentDatabaseType
switch
614 _ => BadDatabaseType(),
616 if (targetVersion <
new Version(4, 8, 0))
617 targetMigration = currentDatabaseType
switch
623 _ => BadDatabaseType(),
625 if (targetVersion <
new Version(4, 7, 0))
626 targetMigration = currentDatabaseType
switch
632 _ => BadDatabaseType(),
634 if (targetVersion <
new Version(4, 6, 0))
635 targetMigration = currentDatabaseType
switch
641 _ => BadDatabaseType(),
643 if (targetVersion <
new Version(4, 5, 0))
644 targetMigration = currentDatabaseType
switch
650 _ => BadDatabaseType(),
652 if (targetVersion <
new Version(4, 4, 0))
653 targetMigration = currentDatabaseType
switch
656 DatabaseType.PostgresSql => nameof(
PGCreate),
659 _ => BadDatabaseType(),
662 if (targetVersion <
new Version(4, 2, 0))
665 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< 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< 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.