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!");
251 return (optionsBuilder, config) => configureFunction.Invoke(
null,
new object[] { optionsBuilder, config });
278 public Task
Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
281 public Task
Drop(CancellationToken cancellationToken) => Database.EnsureDeletedAsync(cancellationToken);
284 public async Task<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);
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(5, 13, 0))
423 targetMigration = currentDatabaseType
switch
429 _ => BadDatabaseType(),
431 if (targetVersion <
new Version(5, 7, 3))
432 targetMigration = currentDatabaseType
switch
438 _ => BadDatabaseType(),
440 if (targetVersion <
new Version(5, 7, 0))
441 targetMigration = currentDatabaseType
switch
447 _ => BadDatabaseType(),
449 if (targetVersion <
new Version(4, 19, 0))
450 targetMigration = currentDatabaseType
switch
456 _ => BadDatabaseType(),
458 if (targetVersion <
new Version(4, 18, 0))
459 targetMigration = currentDatabaseType
switch
465 _ => BadDatabaseType(),
467 if (targetVersion <
new Version(4, 14, 0))
468 targetMigration = currentDatabaseType
switch
474 _ => BadDatabaseType(),
476 if (targetVersion <
new Version(4, 10, 0))
477 targetMigration = currentDatabaseType
switch
483 _ => BadDatabaseType(),
485 if (targetVersion <
new Version(4, 8, 0))
486 targetMigration = currentDatabaseType
switch
492 _ => BadDatabaseType(),
494 if (targetVersion <
new Version(4, 7, 0))
495 targetMigration = currentDatabaseType
switch
501 _ => BadDatabaseType(),
503 if (targetVersion <
new Version(4, 6, 0))
504 targetMigration = currentDatabaseType
switch
510 _ => BadDatabaseType(),
512 if (targetVersion <
new Version(4, 5, 0))
513 targetMigration = currentDatabaseType
switch
519 _ => BadDatabaseType(),
521 if (targetVersion <
new Version(4, 4, 0))
522 targetMigration = currentDatabaseType
switch
525 DatabaseType.PostgresSql => nameof(
PGCreate),
528 _ => BadDatabaseType(),
530 if (targetVersion <
new Version(4, 2, 0))
533 if (targetMigration ==
null)
535 logger.LogDebug(
"No down migration required.");
540 var migrationSubstitution = currentDatabaseType
switch
542 DatabaseType.SqlServer =>
null,
543 DatabaseType.MySql =>
"MY{0}",
544 DatabaseType.Sqlite =>
"SL{0}",
545 DatabaseType.PostgresSql =>
"PG{0}",
546 _ =>
throw new InvalidOperationException($
"Invalid DatabaseType: {currentDatabaseType}"),
549 if (migrationSubstitution !=
null)
550 targetMigration = String.Format(CultureInfo.InvariantCulture, migrationSubstitution, targetMigration[2..]);
553 var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
554 var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
556 logger.LogInformation(
"Migrating down to version {targetVersion}. Target: {targetMigration}", targetVersion, targetMigration);
559 await migrator.MigrateAsync(targetMigration, cancellationToken);
563 logger.LogCritical(e,
"Failed to migrate!");
566#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.
async Task< bool > Migrate(ILogger< DatabaseContext > logger, CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext. A Task<TResult> resulting in true if the database should b...
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.
async Task 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< 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.
readonly IDatabaseCollection< RevisionInformation > revisionInformationsCollection
Backing field for IDatabaseContext.RevisionInformations.
DbSet< TestMerge > TestMerges
The TestMerges in the DatabaseContext.
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 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.
Renames the Heatbeat DreamDaemonSettings to HealthCheck 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 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.
Renames the Heatbeat DreamDaemonSettings to HealthCheck 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 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.
Renames the Heatbeat DreamDaemonSettings to HealthCheck 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 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.
Renames the Heatbeat DreamDaemonSettings to HealthCheck 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 database table.
DatabaseType
Type of database to user.