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; }
246 var configureFunction = typeof(TDatabaseContext).GetMethod(
248 BindingFlags.Public | BindingFlags.Static)
249 ??
throw new InvalidOperationException($
"Context type {typeof(TDatabaseContext).FullName} missing static {ConfigureMethodName} function!");
250 return (optionsBuilder, config) => configureFunction.Invoke(
null,
new object[] { optionsBuilder, config });
258 : base(dbContextOptions)
278 public Task
Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
281 public Task
Drop(CancellationToken cancellationToken) => Database.EnsureDeletedAsync(cancellationToken);
284 public async ValueTask<bool>
Migrate(ILogger<DatabaseContext> logger, CancellationToken cancellationToken)
286 ArgumentNullException.ThrowIfNull(logger);
287 var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken);
288 var wasEmpty = !migrations.Any();
290 if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken)).Any())
292 logger.LogInformation(
"Migrating database...");
293 await Database.MigrateAsync(cancellationToken);
296 logger.LogDebug(
"No migrations to apply");
298 wasEmpty |= !await
Users.AsQueryable().AnyAsync(cancellationToken);
306 ArgumentNullException.ThrowIfNull(modelBuilder);
308 base.OnModelCreating(modelBuilder);
310 var userModel = modelBuilder.Entity<
User>();
311 userModel.HasIndex(x => x.CanonicalName).IsUnique();
312 userModel.HasIndex(x => x.SystemIdentifier).IsUnique();
313 userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
314 userModel.HasMany(x => x.OAuthConnections).WithOne(x => x.User).OnDelete(DeleteBehavior.Cascade);
316 modelBuilder.Entity<
OAuthConnection>().HasIndex(x =>
new { x.Provider, x.ExternalUserId }).IsUnique();
318 var groupsModel = modelBuilder.Entity<
UserGroup>();
319 groupsModel.HasIndex(x => x.Name).IsUnique();
320 groupsModel.HasMany(x => x.Users).WithOne(x => x.Group).OnDelete(DeleteBehavior.ClientSetNull);
322 var permissionSetModel = modelBuilder.Entity<
PermissionSet>();
323 permissionSetModel.HasOne(x => x.Group).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
324 permissionSetModel.HasOne(x => x.User).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
325 permissionSetModel.HasMany(x => x.InstancePermissionSets).WithOne(x => x.PermissionSet).OnDelete(DeleteBehavior.Cascade);
327 modelBuilder.Entity<
InstancePermissionSet>().HasIndex(x =>
new { x.PermissionSetId, x.InstanceId }).IsUnique();
330 revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
331 revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Cascade);
332 revInfo.HasIndex(x =>
new { x.InstanceId, x.CommitSha }).IsUnique();
344 modelBuilder.Entity<
TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.ClientNoAction);
346 var compileJob = modelBuilder.Entity<
CompileJob>();
347 compileJob.HasIndex(x => x.DirectoryName);
348 compileJob.HasOne(x => x.Job).WithOne().OnDelete(DeleteBehavior.Cascade);
350 modelBuilder.Entity<
ReattachInformation>().HasOne(x => x.CompileJob).WithMany().OnDelete(DeleteBehavior.Cascade);
352 var chatChannel = modelBuilder.Entity<
ChatChannel>();
353 chatChannel.HasIndex(x =>
new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
354 chatChannel.HasIndex(x =>
new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
355 chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
357 modelBuilder.Entity<
ChatBot>().HasIndex(x =>
new { x.InstanceId, x.Name }).IsUnique();
359 var instanceModel = modelBuilder.Entity<
Instance>();
360 instanceModel.HasIndex(x =>
new { x.Path, x.SwarmIdentifer }).IsUnique();
361 instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
362 instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
363 instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
364 instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
365 instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
366 instanceModel.HasMany(x => x.InstancePermissionSets).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
367 instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
378 internal static readonly Type MSLatestMigration = typeof(
MSAddTopicPort);
383 internal static readonly Type MYLatestMigration = typeof(
MYAddTopicPort);
388 internal static readonly Type PGLatestMigration = typeof(
PGAddTopicPort);
393 internal static readonly Type SLLatestMigration = typeof(
SLAddTopicPort);
396#pragma warning disable CA1502
398 ILogger<DatabaseContext> logger,
399 Version targetVersion,
401 CancellationToken cancellationToken)
403 ArgumentNullException.ThrowIfNull(logger);
404 ArgumentNullException.ThrowIfNull(targetVersion);
405 if (targetVersion <
new Version(4, 0))
406 throw new ArgumentOutOfRangeException(nameof(targetVersion), targetVersion,
"Cannot migrate below version 4.0.0!");
408 if (currentDatabaseType ==
DatabaseType.PostgresSql && targetVersion <
new Version(4, 3, 0))
409 throw new NotSupportedException(
"Cannot migrate below version 4.3.0 with PostgresSql!");
414 if (targetVersion <
new Version(4, 1, 0))
415 throw new NotSupportedException(
"Cannot migrate below version 4.1.0!");
418 string? targetMigration =
null;
420 string BadDatabaseType() =>
throw new ArgumentException($
"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
422 if (targetVersion <
new Version(6, 0, 0))
423 targetMigration = currentDatabaseType
switch
429 _ => BadDatabaseType(),
431 if (targetVersion <
new Version(5, 17, 0))
432 targetMigration = currentDatabaseType
switch
438 _ => BadDatabaseType(),
440 if (targetVersion <
new Version(5, 13, 0))
441 targetMigration = currentDatabaseType
switch
447 _ => BadDatabaseType(),
449 if (targetVersion <
new Version(5, 7, 3))
450 targetMigration = currentDatabaseType
switch
456 _ => BadDatabaseType(),
458 if (targetVersion <
new Version(5, 7, 0))
459 targetMigration = currentDatabaseType
switch
465 _ => BadDatabaseType(),
467 if (targetVersion <
new Version(4, 19, 0))
468 targetMigration = currentDatabaseType
switch
474 _ => BadDatabaseType(),
476 if (targetVersion <
new Version(4, 18, 0))
477 targetMigration = currentDatabaseType
switch
483 _ => BadDatabaseType(),
485 if (targetVersion <
new Version(4, 14, 0))
486 targetMigration = currentDatabaseType
switch
492 _ => BadDatabaseType(),
494 if (targetVersion <
new Version(4, 10, 0))
495 targetMigration = currentDatabaseType
switch
501 _ => BadDatabaseType(),
503 if (targetVersion <
new Version(4, 8, 0))
504 targetMigration = currentDatabaseType
switch
510 _ => BadDatabaseType(),
512 if (targetVersion <
new Version(4, 7, 0))
513 targetMigration = currentDatabaseType
switch
519 _ => BadDatabaseType(),
521 if (targetVersion <
new Version(4, 6, 0))
522 targetMigration = currentDatabaseType
switch
528 _ => BadDatabaseType(),
530 if (targetVersion <
new Version(4, 5, 0))
531 targetMigration = currentDatabaseType
switch
537 _ => BadDatabaseType(),
539 if (targetVersion <
new Version(4, 4, 0))
540 targetMigration = currentDatabaseType
switch
543 DatabaseType.PostgresSql => nameof(
PGCreate),
546 _ => BadDatabaseType(),
548 if (targetVersion <
new Version(4, 2, 0))
551 if (targetMigration ==
null)
553 logger.LogDebug(
"No down migration required.");
558 var migrationSubstitution = currentDatabaseType
switch
560 DatabaseType.SqlServer =>
null,
561 DatabaseType.MySql =>
"MY{0}",
562 DatabaseType.Sqlite =>
"SL{0}",
563 DatabaseType.PostgresSql =>
"PG{0}",
564 _ =>
throw new InvalidOperationException($
"Invalid DatabaseType: {currentDatabaseType}"),
567 if (migrationSubstitution !=
null)
568 targetMigration = String.Format(CultureInfo.InvariantCulture, migrationSubstitution, targetMigration[2..]);
571 var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
572 var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
574 logger.LogInformation(
"Migrating down to version {targetVersion}. Target: {targetMigration}", targetVersion, targetMigration);
577 await migrator.MigrateAsync(targetMigration, cancellationToken);
581 logger.LogCritical(e,
"Failed to migrate!");
584#pragma warning restore CA1502
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< 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 sho...
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.
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.
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.