From e7424f27431d34f153855dcc783c8e91d7b4ad2b Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 16 Jul 2026 09:09:01 +1000 Subject: [PATCH 1/6] Added the skeleton projects for EF --- src/Directory.Packages.props | 6 + ...ProjectReferences.Persisters.Primary.props | 2 + .../PostgreSqlPersistence.cs | 32 +++++ .../PostgreSqlPersistenceConfiguration.cs | 12 ++ .../PostgreSqlPersisterSettings.cs | 7 ++ .../PostgreSqlServiceControlDbContext.cs | 8 ++ ...ostgreSqlServiceControlDbContextFactory.cs | 21 ++++ ...ntrol.Persistence.EFCore.PostgreSQL.csproj | 37 ++++++ .../persistence.manifest | 25 ++++ ...ontrol.Persistence.EFCore.SqlServer.csproj | 37 ++++++ .../SqlServerPersistence.cs | 32 +++++ .../SqlServerPersistenceConfiguration.cs | 12 ++ .../SqlServerPersisterSettings.cs | 7 ++ .../SqlServerServiceControlDbContext.cs | 8 ++ ...SqlServerServiceControlDbContextFactory.cs | 21 ++++ .../persistence.manifest | 25 ++++ .../Abstractions/BasePersistence.cs | 50 ++++++++ .../EFPersistenceConfigurationBase.cs | 40 +++++++ .../Abstractions/EFPersisterSettings.cs | 10 ++ .../DbContexts/ServiceControlDbContext.cs | 7 ++ .../Implementation/BodyStorage.cs | 9 ++ .../Implementation/CustomCheckDataStore.cs | 19 +++ .../EditFailedMessagesManager.cs | 27 +++++ .../Implementation/EndpointSettingsStore.cs | 13 ++ .../Implementation/ErrorMessagesDataStore.cs | 111 ++++++++++++++++++ .../Implementation/EventLogDataStore.cs | 13 ++ .../ExternalIntegrationRequestsDataStore.cs | 19 +++ .../FailedErrorImportDataStore.cs | 12 ++ .../FailedMessageViewIndexNotifications.cs | 15 +++ .../Implementation/GroupsDataStore.cs | 12 ++ .../Implementation/LicensingDataStore.cs | 21 ++++ .../Implementation/MessageArchiver.cs | 32 +++++ .../MessageRedirectsDataStore.cs | 12 ++ .../Implementation/MonitoringDataStore.cs | 24 ++++ .../Implementation/NotificationsManager.cs | 18 +++ .../Implementation/QueueAddressStore.cs | 13 ++ .../Implementation/RetryBatchesDataStore.cs | 21 ++++ .../Implementation/RetryBatchesManager.cs | 50 ++++++++ .../Implementation/RetryDocumentDataStore.cs | 41 +++++++ .../Implementation/RetryHistoryDataStore.cs | 16 +++ .../Implementation/SubscriptionStorage.cs | 20 ++++ .../TrialLicenseDataProvider.cs | 10 ++ .../UnitOfWork/EFIngestionUnitOfWork.cs | 21 ++++ .../EFIngestionUnitOfWorkFactory.cs | 12 ++ .../EFMonitoringIngestionUnitOfWork.cs | 9 ++ .../EFRecoverabilityIngestionUnitOfWork.cs | 16 +++ .../ServiceControl.Persistence.EFCore.csproj | 27 +++++ .../DevelopmentPersistenceLocations.cs | 2 + src/ServiceControl.slnx | 3 + 49 files changed, 1017 insertions(+) create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest create mode 100644 src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs create mode 100644 src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs create mode 100644 src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs create mode 100644 src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index c353458ebe..e44237b5a9 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -30,6 +30,12 @@ + + + + + + diff --git a/src/ProjectReferences.Persisters.Primary.props b/src/ProjectReferences.Persisters.Primary.props index 255b45ed5c..a831563338 100644 --- a/src/ProjectReferences.Persisters.Primary.props +++ b/src/ProjectReferences.Persisters.Primary.props @@ -1,6 +1,8 @@ + + diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs new file mode 100644 index 0000000000..3f5e30350e --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs @@ -0,0 +1,32 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSQL; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using ServiceControl.Persistence.EFCore.Abstractions; + +class PostgreSqlPersistence(PostgreSqlPersisterSettings settings) : BasePersistence, IPersistence +{ + public void AddPersistence(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + RegisterDataStores(services); + } + + public void AddInstaller(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + } + + void RegisterSettings(IServiceCollection services) + { + services.AddSingleton(settings); + services.AddSingleton(settings); + services.AddSingleton(settings); + } + + void ConfigureDbContext(IServiceCollection services) => + services.AddPooledDbContextFactory(options => + options.UseNpgsql(settings.ConnectionString, npgsql => npgsql.CommandTimeout(settings.CommandTimeout))); +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs new file mode 100644 index 0000000000..478016bf6a --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSQL; + +using ServiceControl.Persistence.EFCore.Abstractions; + +class PostgreSqlPersistenceConfiguration : EFPersistenceConfigurationBase +{ + public override IPersistence Create(PersistenceSettings settings) => + new PostgreSqlPersistence((PostgreSqlPersisterSettings)settings); + + protected override EFPersisterSettings CreateSettings(string connectionString) => + new PostgreSqlPersisterSettings { ConnectionString = connectionString }; +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs new file mode 100644 index 0000000000..eae9ce625a --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs @@ -0,0 +1,7 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSQL; + +using ServiceControl.Persistence.EFCore.Abstractions; + +public class PostgreSqlPersisterSettings : EFPersisterSettings +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs new file mode 100644 index 0000000000..24088c23c6 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs @@ -0,0 +1,8 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSQL; + +using Microsoft.EntityFrameworkCore; +using ServiceControl.Persistence.EFCore.DbContexts; + +public class PostgreSqlServiceControlDbContext(DbContextOptions options) : ServiceControlDbContext(options) +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs new file mode 100644 index 0000000000..853a6500bd --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSQL; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +/// +/// Design-time factory used by the dotnet-ef tooling (e.g. dotnet ef migrations add). +/// +public class PostgreSqlServiceControlDbContextFactory : IDesignTimeDbContextFactory +{ + public PostgreSqlServiceControlDbContext CreateDbContext(string[] args) + { + var connectionString = Environment.GetEnvironmentVariable("SERVICECONTROL_DATABASE_CONNECTIONSTRING") + ?? "Host=localhost;Port=5432;Database=servicecontrol;Username=postgres;Password=postgres"; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseNpgsql(connectionString); + + return new PostgreSqlServiceControlDbContext(optionsBuilder.Options); + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj b/src/ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj new file mode 100644 index 0000000000..f638c069d0 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest b/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest new file mode 100644 index 0000000000..3b60cd93cd --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest @@ -0,0 +1,25 @@ +{ + "Name": "PostgreSQL", + "DisplayName": "PostgreSQL", + "Description": "PostgreSQL ServiceControl persister", + "AssemblyName": "ServiceControl.Persistence.EFCore.PostgreSQL", + "TypeName": "ServiceControl.Persistence.EFCore.PostgreSQL.PostgreSqlPersistenceConfiguration, ServiceControl.Persistence.EFCore.PostgreSQL", + "Settings": [ + { + "Name": "ServiceControl/Database/ConnectionString", + "Mandatory": true + }, + { + "Name": "ServiceControl/Database/CommandTimeout", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/StoragePath", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/MinCompressionSize", + "Mandatory": false + } + ] +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj b/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj new file mode 100644 index 0000000000..db51cbb614 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs new file mode 100644 index 0000000000..afbf49c124 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs @@ -0,0 +1,32 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using ServiceControl.Persistence.EFCore.Abstractions; + +class SqlServerPersistence(SqlServerPersisterSettings settings) : BasePersistence, IPersistence +{ + public void AddPersistence(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + RegisterDataStores(services); + } + + public void AddInstaller(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + } + + void RegisterSettings(IServiceCollection services) + { + services.AddSingleton(settings); + services.AddSingleton(settings); + services.AddSingleton(settings); + } + + void ConfigureDbContext(IServiceCollection services) => + services.AddPooledDbContextFactory(options => + options.UseSqlServer(settings.ConnectionString, sqlServer => sqlServer.CommandTimeout(settings.CommandTimeout))); +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs new file mode 100644 index 0000000000..dc9f3c0f3d --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using ServiceControl.Persistence.EFCore.Abstractions; + +class SqlServerPersistenceConfiguration : EFPersistenceConfigurationBase +{ + public override IPersistence Create(PersistenceSettings settings) => + new SqlServerPersistence((SqlServerPersisterSettings)settings); + + protected override EFPersisterSettings CreateSettings(string connectionString) => + new SqlServerPersisterSettings { ConnectionString = connectionString }; +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs new file mode 100644 index 0000000000..9f3bd871b4 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs @@ -0,0 +1,7 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using ServiceControl.Persistence.EFCore.Abstractions; + +public class SqlServerPersisterSettings : EFPersisterSettings +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs new file mode 100644 index 0000000000..528ab19b52 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs @@ -0,0 +1,8 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.EntityFrameworkCore; +using ServiceControl.Persistence.EFCore.DbContexts; + +public class SqlServerServiceControlDbContext(DbContextOptions options) : ServiceControlDbContext(options) +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs new file mode 100644 index 0000000000..2be9b44199 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +/// +/// Design-time factory used by the dotnet-ef tooling (e.g. dotnet ef migrations add). +/// +public class SqlServerServiceControlDbContextFactory : IDesignTimeDbContextFactory +{ + public SqlServerServiceControlDbContext CreateDbContext(string[] args) + { + var connectionString = Environment.GetEnvironmentVariable("SERVICECONTROL_DATABASE_CONNECTIONSTRING") + ?? "Server=localhost;Database=ServiceControl;Trusted_Connection=True;TrustServerCertificate=True"; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + + return new SqlServerServiceControlDbContext(optionsBuilder.Options); + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest b/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest new file mode 100644 index 0000000000..670558f97b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest @@ -0,0 +1,25 @@ +{ + "Name": "SqlServer", + "DisplayName": "SQL Server", + "Description": "SQL Server ServiceControl persister", + "AssemblyName": "ServiceControl.Persistence.EFCore.SqlServer", + "TypeName": "ServiceControl.Persistence.EFCore.SqlServer.SqlServerPersistenceConfiguration, ServiceControl.Persistence.EFCore.SqlServer", + "Settings": [ + { + "Name": "ServiceControl/Database/ConnectionString", + "Mandatory": true + }, + { + "Name": "ServiceControl/Database/CommandTimeout", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/StoragePath", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/MinCompressionSize", + "Mandatory": false + } + ] +} diff --git a/src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs b/src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs new file mode 100644 index 0000000000..0076c9ae81 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs @@ -0,0 +1,50 @@ +namespace ServiceControl.Persistence.EFCore.Abstractions; + +using Microsoft.Extensions.DependencyInjection; +using NServiceBus.Unicast.Subscriptions.MessageDrivenSubscriptions; +using Particular.LicensingComponent.Persistence; +using ServiceControl.Operations.BodyStorage; +using ServiceControl.Persistence.EFCore.Implementation; +using ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; +using ServiceControl.Persistence.MessageRedirects; +using ServiceControl.Persistence.Recoverability; +using ServiceControl.Persistence.UnitOfWork; + +public abstract class BasePersistence +{ + protected static void RegisterDataStores(IServiceCollection services) + { + services.AddSingleton(TimeProvider.System); + + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); + + services.AddUnitOfWorkFactory(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); + services.AddHostedService(p => p.GetRequiredService()); + + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); + services.AddHostedService(p => p.GetRequiredService()); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs new file mode 100644 index 0000000000..54f272d30c --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs @@ -0,0 +1,40 @@ +namespace ServiceControl.Persistence.EFCore.Abstractions; + +using ServiceControl.Configuration; + +public abstract class EFPersistenceConfigurationBase : IPersistenceConfiguration +{ + const string ConnectionStringKey = "Database/ConnectionString"; + const string CommandTimeoutKey = "Database/CommandTimeout"; + const string MessageBodyStoragePathKey = "MessageBody/StoragePath"; + const string MinBodySizeForCompressionKey = "MessageBody/MinCompressionSize"; + const string ErrorRetentionPeriodKey = "ErrorRetentionPeriod"; + const string EnableFullTextSearchOnBodiesKey = "EnableFullTextSearchOnBodies"; + + public PersistenceSettings CreateSettings(SettingsRootNamespace settingsRootNamespace) + { + var settings = CreateSettings(GetRequiredSetting(settingsRootNamespace, ConnectionStringKey)); + + settings.CommandTimeout = SettingsReader.Read(settingsRootNamespace, CommandTimeoutKey, 30); + settings.MessageBodyStoragePath = SettingsReader.Read(settingsRootNamespace, MessageBodyStoragePathKey); + settings.MinBodySizeForCompression = SettingsReader.Read(settingsRootNamespace, MinBodySizeForCompressionKey, 4096); + settings.ErrorRetentionPeriod = GetRequiredSetting(settingsRootNamespace, ErrorRetentionPeriodKey); + settings.EnableFullTextSearchOnBodies = SettingsReader.Read(settingsRootNamespace, EnableFullTextSearchOnBodiesKey, true); + + return settings; + } + + public abstract IPersistence Create(PersistenceSettings settings); + + protected abstract EFPersisterSettings CreateSettings(string connectionString); + + static T GetRequiredSetting(SettingsRootNamespace settingsRootNamespace, string key) + { + if (SettingsReader.TryRead(settingsRootNamespace, key, out var value)) + { + return value; + } + + throw new Exception($"Setting {key} of type {typeof(T)} is required"); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs new file mode 100644 index 0000000000..6789116fb7 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs @@ -0,0 +1,10 @@ +namespace ServiceControl.Persistence.EFCore.Abstractions; + +public abstract class EFPersisterSettings : PersistenceSettings +{ + public required string ConnectionString { get; set; } + public int CommandTimeout { get; set; } = 30; + public TimeSpan ErrorRetentionPeriod { get; set; } + public string? MessageBodyStoragePath { get; set; } + public int MinBodySizeForCompression { get; set; } = 4096; +} diff --git a/src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs new file mode 100644 index 0000000000..83f28e3310 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs @@ -0,0 +1,7 @@ +namespace ServiceControl.Persistence.EFCore.DbContexts; + +using Microsoft.EntityFrameworkCore; + +public abstract class ServiceControlDbContext(DbContextOptions options) : DbContext(options) +{ +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs b/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs new file mode 100644 index 0000000000..e1caa893a7 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs @@ -0,0 +1,9 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Operations.BodyStorage; + +public class BodyStorage : IBodyStorage +{ + public Task TryFetch(string bodyId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs new file mode 100644 index 0000000000..9a16177f3d --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs @@ -0,0 +1,19 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Contracts.CustomChecks; +using ServiceControl.Persistence.Infrastructure; + +public class CustomCheckDataStore : ICustomChecksDataStore +{ + public Task UpdateCustomCheckStatus(CustomCheckDetail detail) => + throw new NotImplementedException(); + + public Task>> GetStats(PagingInfo paging, string? status = null) => + throw new NotImplementedException(); + + public Task DeleteCustomCheck(Guid id) => + throw new NotImplementedException(); + + public Task GetNumberOfFailedChecks() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs new file mode 100644 index 0000000000..aa936dd6dc --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs @@ -0,0 +1,27 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; + +public class EditFailedMessagesManager : IEditFailedMessagesManager +{ + public Task GetFailedMessage(string failedMessageId) => + throw new NotImplementedException(); + + public Task GetCurrentEditingRequestId(string failedMessageId) => + throw new NotImplementedException(); + + public Task SetCurrentEditingRequestId(string editingMessageId) => + throw new NotImplementedException(); + + public Task SetFailedMessageAsResolved() => + throw new NotImplementedException(); + + public Task SaveChanges() => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs new file mode 100644 index 0000000000..7e79541e91 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs @@ -0,0 +1,13 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +public class EndpointSettingsStore : IEndpointSettingsStore +{ + public IAsyncEnumerable GetAllEndpointSettings() => + throw new NotImplementedException(); + + public Task UpdateEndpointSettings(EndpointSettings settings, CancellationToken token) => + throw new NotImplementedException(); + + public Task Delete(string name, CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs new file mode 100644 index 0000000000..2757c59658 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs @@ -0,0 +1,111 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.CompositeViews.Messages; +using ServiceControl.EventLog; +using ServiceControl.MessageFailures; +using ServiceControl.MessageFailures.Api; +using ServiceControl.Operations; +using ServiceControl.Persistence.Infrastructure; +using ServiceControl.Recoverability; + +public class ErrorMessagesDataStore : IErrorMessageDataStore +{ + public Task>> GetAllMessages(PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task>> GetAllMessagesForEndpoint(string endpointName, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task>> GetAllMessagesByConversation(string conversationId, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages) => + throw new NotImplementedException(); + + public Task>> GetAllMessagesForSearch(string searchTerms, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task>> SearchEndpointMessages(string endpointName, string searchKeyword, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task FailedMessageMarkAsArchived(string failedMessageId) => + throw new NotImplementedException(); + + public Task FailedMessagesFetch(Guid[] ids) => + throw new NotImplementedException(); + + public Task StoreFailedErrorImport(FailedErrorImport failure) => + throw new NotImplementedException(); + + public Task CreateEditFailedMessageManager() => + throw new NotImplementedException(); + + public Task> GetFailureGroupView(string groupId, string status, string modified) => + throw new NotImplementedException(); + + public Task> GetFailureGroupsByClassifier(string classifier) => + throw new NotImplementedException(); + + public Task>> ErrorGet(string status, string modified, string queueAddress, PagingInfo pagingInfo, SortInfo sortInfo) => + throw new NotImplementedException(); + + public Task ErrorsHead(string status, string modified, string queueAddress) => + throw new NotImplementedException(); + + public Task>> ErrorsByEndpointName(string status, string endpointName, string modified, PagingInfo pagingInfo, SortInfo sortInfo) => + throw new NotImplementedException(); + + public Task> ErrorsSummary() => + throw new NotImplementedException(); + + public Task ErrorLastBy(string failedMessageId) => + throw new NotImplementedException(); + + public Task ErrorBy(string failedMessageId) => + throw new NotImplementedException(); + + public Task CreateNotificationsManager() => + throw new NotImplementedException(); + + public Task EditComment(string groupId, string comment) => + throw new NotImplementedException(); + + public Task DeleteComment(string groupId) => + throw new NotImplementedException(); + + public Task>> GetGroupErrors(string groupId, string status, string modified, SortInfo sortInfo, PagingInfo pagingInfo) => + throw new NotImplementedException(); + + public Task GetGroupErrorsCount(string groupId, string status, string modified) => + throw new NotImplementedException(); + + public Task>> GetGroup(string groupId, string status, string modified) => + throw new NotImplementedException(); + + public Task MarkMessageAsResolved(string failedMessageId) => + throw new NotImplementedException(); + + public Task ProcessPendingRetries(DateTime periodFrom, DateTime periodTo, string queueAddress, Func processCallback) => + throw new NotImplementedException(); + + public Task UnArchiveMessagesByRange(DateTime from, DateTime to) => + throw new NotImplementedException(); + + public Task UnArchiveMessages(IEnumerable failedMessageIds) => + throw new NotImplementedException(); + + public Task RevertRetry(string messageUniqueId) => + throw new NotImplementedException(); + + public Task RemoveFailedMessageRetryDocument(string uniqueMessageId) => + throw new NotImplementedException(); + + public Task GetRetryPendingMessages(DateTime from, DateTime to, string queueAddress) => + throw new NotImplementedException(); + + public Task FetchFromFailedMessage(string uniqueMessageId) => + throw new NotImplementedException(); + + public Task StoreEventLogItem(EventLogItem logItem) => + throw new NotImplementedException(); + + public Task StoreFailedMessagesForTestsOnly(params FailedMessage[] failedMessages) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs new file mode 100644 index 0000000000..1b9799b989 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs @@ -0,0 +1,13 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.EventLog; +using ServiceControl.Persistence.Infrastructure; + +public class EventLogDataStore : IEventLogDataStore +{ + public Task Add(EventLogItem logItem) => + throw new NotImplementedException(); + + public Task<(IList items, long total, string version)> GetEventLogItems(PagingInfo pagingInfo) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs new file mode 100644 index 0000000000..dfa5094631 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs @@ -0,0 +1,19 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using Microsoft.Extensions.Hosting; +using ServiceControl.ExternalIntegrations; + +public class ExternalIntegrationRequestsDataStore : IExternalIntegrationRequestsDataStore, IHostedService +{ + public void Subscribe(Func callback) => + throw new NotImplementedException(); + + public Task StoreDispatchRequest(IEnumerable dispatchRequests) => + throw new NotImplementedException(); + + public Task StartAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task StopAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs new file mode 100644 index 0000000000..ed2dd126db --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Operations; + +public class FailedErrorImportDataStore : IFailedErrorImportDataStore +{ + public Task ProcessFailedErrorImports(Func processMessage, CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task QueryContainsFailedImports() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs new file mode 100644 index 0000000000..583e6de887 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs @@ -0,0 +1,15 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using Microsoft.Extensions.Hosting; + +public class FailedMessageViewIndexNotifications : IFailedMessageViewIndexNotifications, IHostedService +{ + public IDisposable Subscribe(Func callback) => + throw new NotImplementedException(); + + public Task StartAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task StopAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs new file mode 100644 index 0000000000..41b18e12df --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Recoverability; + +public class GroupsDataStore : IGroupsDataStore +{ + public Task> GetFailureGroupsByClassifier(string classifier, string classifierFilter) => + throw new NotImplementedException(); + + public Task GetCurrentForwardingBatch() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs new file mode 100644 index 0000000000..03a0c1e6fe --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs @@ -0,0 +1,21 @@ +using Particular.LicensingComponent.Contracts; +using Particular.LicensingComponent.Persistence; + +class LicensingDataStore : ILicensingDataStore +{ + public Task> GetAllEndpoints(bool includePlatformEndpoints, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task GetAuditServiceMetadata(CancellationToken cancellationToken = default) => throw new NotImplementedException(); + public Task GetBrokerMetadata(CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task GetEndpoint(EndpointIdentifier id, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + public Task> GetEndpoints(IList endpointIds, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task>> GetEndpointThroughputByQueueName(IList queueNames, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task> GetReportMasks(CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task IsThereThroughputForLastXDays(int days, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task IsThereThroughputForLastXDaysForSource(int days, ThroughputSource throughputSource, bool includeToday, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task RecordEndpointThroughput(string endpointName, ThroughputSource throughputSource, IList throughput, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveAuditServiceMetadata(AuditServiceMetadata auditServiceMetadata, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveBrokerMetadata(BrokerMetadata brokerMetadata, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveEndpoint(Particular.LicensingComponent.Contracts.Endpoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveReportMasks(List reportMasks, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task UpdateUserIndicatorOnEndpoints(List userIndicatorUpdates, CancellationToken cancellationToken) => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs b/src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs new file mode 100644 index 0000000000..9ec470e855 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs @@ -0,0 +1,32 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Infrastructure.Auth; +using ServiceControl.Persistence.Recoverability; +using ServiceControl.Recoverability; + +public class MessageArchiver : IArchiveMessages +{ + public Task ArchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string? operationId = null) => + throw new NotImplementedException(); + + public Task UnarchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string? operationId = null) => + throw new NotImplementedException(); + + public bool IsOperationInProgressFor(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public bool IsArchiveInProgressFor(string groupId) => + throw new NotImplementedException(); + + public void DismissArchiveOperation(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public Task StartArchiving(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public Task StartUnarchiving(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public IEnumerable GetArchivalOperations() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs new file mode 100644 index 0000000000..d13276f822 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Persistence.MessageRedirects; + +public class MessageRedirectsDataStore : IMessageRedirectsDataStore +{ + public Task GetOrCreate() => + throw new NotImplementedException(); + + public Task Save(MessageRedirectsCollection redirects) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs new file mode 100644 index 0000000000..d6ce70598f --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs @@ -0,0 +1,24 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Operations; + +public class MonitoringDataStore : IMonitoringDataStore +{ + public Task CreateIfNotExists(EndpointDetails endpoint) => + throw new NotImplementedException(); + + public Task CreateOrUpdate(EndpointDetails endpoint, IEndpointInstanceMonitoring endpointInstanceMonitoring) => + throw new NotImplementedException(); + + public Task UpdateEndpointMonitoring(EndpointDetails endpoint, bool isMonitored) => + throw new NotImplementedException(); + + public Task WarmupMonitoringFromPersistence(IEndpointInstanceMonitoring endpointInstanceMonitoring) => + throw new NotImplementedException(); + + public Task Delete(Guid endpointId) => + throw new NotImplementedException(); + + public Task> GetAllKnownEndpoints() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs b/src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs new file mode 100644 index 0000000000..74dabd8071 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs @@ -0,0 +1,18 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Notifications; + +public class NotificationsManager : INotificationsManager +{ + public Task LoadSettings(TimeSpan? cacheTimeout = null) => + throw new NotImplementedException(); + + public Task SaveChanges() => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs new file mode 100644 index 0000000000..b53673b630 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs @@ -0,0 +1,13 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.Infrastructure; + +public class QueueAddressStore : IQueueAddressStore +{ + public Task>> GetAddresses(PagingInfo pagingInfo) => + throw new NotImplementedException(); + + public Task>> GetAddressesBySearchTerm(string search, PagingInfo pagingInfo) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs new file mode 100644 index 0000000000..ded8d774a8 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Recoverability; + +public class RetryBatchesDataStore : IRetryBatchesDataStore +{ + public Task CreateRetryBatchesManager() => + throw new NotImplementedException(); + + public Task RecordFailedStagingAttempt(IReadOnlyCollection messages, + IReadOnlyDictionary failedMessageRetriesById, Exception e, + int maxStagingAttempts, string stagingId) => + throw new NotImplementedException(); + + public Task IncrementAttemptCounter(FailedMessageRetry failedMessageRetry) => + throw new NotImplementedException(); + + public Task DeleteFailedMessageRetry(string makeDocumentId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs new file mode 100644 index 0000000000..9348fe0ab8 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs @@ -0,0 +1,50 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.MessageRedirects; +using ServiceControl.Recoverability; + +public class RetryBatchesManager : IRetryBatchesManager +{ + public void Delete(RetryBatch retryBatch) => + throw new NotImplementedException(); + + public void Delete(RetryBatchNowForwarding forwardingBatch) => + throw new NotImplementedException(); + + public Task GetFailedMessageRetries(IList stagingBatchFailureRetries) => + throw new NotImplementedException(); + + public void Evict(FailedMessageRetry failedMessageRetry) => + throw new NotImplementedException(); + + public Task GetFailedMessages(Dictionary.KeyCollection keys) => + throw new NotImplementedException(); + + public Task GetRetryBatchNowForwarding() => + throw new NotImplementedException(); + + public Task GetRetryBatch(string retryBatchId, CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task GetStagingBatch() => + throw new NotImplementedException(); + + public Task Store(RetryBatchNowForwarding retryBatchNowForwarding) => + throw new NotImplementedException(); + + public Task GetOrCreateMessageRedirectsCollection() => + throw new NotImplementedException(); + + public Task CancelExpiration(FailedMessage failedMessage) => + throw new NotImplementedException(); + + public Task SaveChanges() => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs new file mode 100644 index 0000000000..f29758b8a5 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs @@ -0,0 +1,41 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.Infrastructure; +using ServiceControl.Recoverability; + +public class RetryDocumentDataStore : IRetryDocumentDataStore +{ + public Task StageRetryByUniqueMessageIds(string batchDocumentId, string[] messageIds) => + throw new NotImplementedException(); + + public Task MoveBatchToStaging(string batchDocumentId) => + throw new NotImplementedException(); + + public Task CreateBatchDocument(string retrySessionId, string requestId, RetryType retryType, + string[] failedMessageRetryIds, string originator, DateTime startTime, DateTime? last = null, + string? batchName = null, string? classifier = null, + string? initiatedById = null, string? initiatedByName = null, string? operationId = null) => + throw new NotImplementedException(); + + public Task>> QueryOrphanedBatches(string retrySessionId) => + throw new NotImplementedException(); + + public Task> QueryAvailableBatches() => + throw new NotImplementedException(); + + public Task GetBatchesForAll(DateTime cutoff, Func callback) => + throw new NotImplementedException(); + + public Task GetBatchesForEndpoint(DateTime cutoff, string endpoint, Func callback) => + throw new NotImplementedException(); + + public Task GetBatchesForFailedQueueAddress(DateTime cutoff, string failedQueueAddresspoint, FailedMessageStatus status, Func callback) => + throw new NotImplementedException(); + + public Task GetBatchesForFailureGroup(string groupId, string groupTitle, string groupType, DateTime cutoff, Func callback) => + throw new NotImplementedException(); + + public Task QueryFailureGroupViewOnGroupId(string groupId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs new file mode 100644 index 0000000000..bd514be05b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs @@ -0,0 +1,16 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Recoverability; + +public class RetryHistoryDataStore : IRetryHistoryDataStore +{ + public Task GetRetryHistory() => + throw new NotImplementedException(); + + public Task RecordRetryOperationCompleted(string requestId, RetryType retryType, DateTime startTime, DateTime completionTime, + string originator, string classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, int retryHistoryDepth) => + throw new NotImplementedException(); + + public Task AcknowledgeRetryGroup(string groupId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs b/src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs new file mode 100644 index 0000000000..6b4d8c6e93 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs @@ -0,0 +1,20 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using NServiceBus.Extensibility; +using NServiceBus.Unicast.Subscriptions; +using NServiceBus.Unicast.Subscriptions.MessageDrivenSubscriptions; + +public class SubscriptionStorage : IServiceControlSubscriptionStorage +{ + public Task Initialize() => + throw new NotImplementedException(); + + public Task Subscribe(Subscriber subscriber, MessageType messageType, ContextBag context, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + public Task Unsubscribe(Subscriber subscriber, MessageType messageType, ContextBag context, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + public Task> GetSubscriberAddressesForMessage(IEnumerable messageTypes, ContextBag context, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs b/src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs new file mode 100644 index 0000000000..e10739ee89 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs @@ -0,0 +1,10 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +public class TrialLicenseDataProvider : ITrialLicenseDataProvider +{ + public Task GetTrialEndDate(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task StoreTrialEndDate(DateOnly trialEndDate, CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs new file mode 100644 index 0000000000..2e0b2ab1ae --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using ServiceControl.Persistence.UnitOfWork; + +public class EFIngestionUnitOfWork : IIngestionUnitOfWork +{ + public IMonitoringIngestionUnitOfWork Monitoring => + throw new NotImplementedException(); + + public IRecoverabilityIngestionUnitOfWork Recoverability => + throw new NotImplementedException(); + + public Task Complete(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs new file mode 100644 index 0000000000..2aa8efe4c6 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using ServiceControl.Persistence.UnitOfWork; + +public class EFIngestionUnitOfWorkFactory : IIngestionUnitOfWorkFactory +{ + public ValueTask StartNew() => + throw new NotImplementedException(); + + public bool CanIngestMore() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs new file mode 100644 index 0000000000..af7080b6a5 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs @@ -0,0 +1,9 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using ServiceControl.Persistence.UnitOfWork; + +public class EFMonitoringIngestionUnitOfWork : IMonitoringIngestionUnitOfWork +{ + public Task RecordKnownEndpoint(KnownEndpoint knownEndpoint) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs new file mode 100644 index 0000000000..7e7e93f63e --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs @@ -0,0 +1,16 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using NServiceBus.Transport; +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.UnitOfWork; + +public class EFRecoverabilityIngestionUnitOfWork : IRecoverabilityIngestionUnitOfWork +{ + public Task RecordFailedProcessingAttempt(MessageContext context, + FailedMessage.ProcessingAttempt processingAttempt, + List groups) => + throw new NotImplementedException(); + + public Task RecordSuccessfulRetry(string retriedMessageUniqueId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj b/src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj new file mode 100644 index 0000000000..f80428271a --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj @@ -0,0 +1,27 @@ + + + + net10.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + diff --git a/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs b/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs index 451009ee88..015552e16a 100644 --- a/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs +++ b/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs @@ -18,6 +18,8 @@ static DevelopmentPersistenceLocations() if (!string.IsNullOrWhiteSpace(srcFolder) && srcFolder.EndsWith("src")) { ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Persistence.RavenDB")); + ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Persistence.EFCore.SqlServer")); + ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Persistence.EFCore.PostgreSQL")); } } diff --git a/src/ServiceControl.slnx b/src/ServiceControl.slnx index fc2f5dd384..5d48bcf3d5 100644 --- a/src/ServiceControl.slnx +++ b/src/ServiceControl.slnx @@ -41,6 +41,9 @@ + + + From 20898a15ea13f12f2497812162c284bd5644beeb Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 16 Jul 2026 11:42:23 +1000 Subject: [PATCH 2/6] Fixing tests --- src/Directory.Packages.props | 2 ++ ...ManifestTests.ApprovePrimaryInstanceManifests.approved.txt | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index e44237b5a9..e0e28c4942 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -98,6 +98,8 @@ + + diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt index 59a50c03c0..cedca1823f 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt +++ b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt @@ -1,4 +1,6 @@ [ + "PostgreSQL: PostgreSQL", "RavenDB: RavenDB", - "RavenDB35: RavenDB 3.5 (Legacy)" + "RavenDB35: RavenDB 3.5 (Legacy)", + "SqlServer: SqlServer" ] \ No newline at end of file From d85d4ec0f2becf5b528ae79001d91f5c2079bdb0 Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 16 Jul 2026 11:54:53 +1000 Subject: [PATCH 3/6] Fixing casing to match existing naming pattern --- .../PostgreSqlPersistence.cs | 2 +- .../PostgreSqlPersistenceConfiguration.cs | 2 +- .../PostgreSqlPersisterSettings.cs | 2 +- .../PostgreSqlServiceControlDbContext.cs | 2 +- .../PostgreSqlServiceControlDbContextFactory.cs | 2 +- .../persistence.manifest | 4 ++-- .../persistence.manifest | 2 +- src/ServiceControl.slnx | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs index 3f5e30350e..0f3f01ad9a 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.EFCore.PostgreSQL; +namespace ServiceControl.Persistence.EFCore.PostgreSql; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs index 478016bf6a..ad490439af 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.EFCore.PostgreSQL; +namespace ServiceControl.Persistence.EFCore.PostgreSql; using ServiceControl.Persistence.EFCore.Abstractions; diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs index eae9ce625a..88cba1ef04 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.EFCore.PostgreSQL; +namespace ServiceControl.Persistence.EFCore.PostgreSql; using ServiceControl.Persistence.EFCore.Abstractions; diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs index 24088c23c6..3f7baa435e 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.EFCore.PostgreSQL; +namespace ServiceControl.Persistence.EFCore.PostgreSql; using Microsoft.EntityFrameworkCore; using ServiceControl.Persistence.EFCore.DbContexts; diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs index 853a6500bd..5a17acb93f 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.EFCore.PostgreSQL; +namespace ServiceControl.Persistence.EFCore.PostgreSql; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest b/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest index 3b60cd93cd..925712ab78 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest +++ b/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest @@ -2,8 +2,8 @@ "Name": "PostgreSQL", "DisplayName": "PostgreSQL", "Description": "PostgreSQL ServiceControl persister", - "AssemblyName": "ServiceControl.Persistence.EFCore.PostgreSQL", - "TypeName": "ServiceControl.Persistence.EFCore.PostgreSQL.PostgreSqlPersistenceConfiguration, ServiceControl.Persistence.EFCore.PostgreSQL", + "AssemblyName": "ServiceControl.Persistence.EFCore.PostgreSql", + "TypeName": "ServiceControl.Persistence.EFCore.PostgreSql.PostgreSqlPersistenceConfiguration, ServiceControl.Persistence.EFCore.PostgreSql", "Settings": [ { "Name": "ServiceControl/Database/ConnectionString", diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest b/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest index 670558f97b..aa1b57b88f 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest @@ -1,5 +1,5 @@ { - "Name": "SqlServer", + "Name": "SQLServer", "DisplayName": "SQL Server", "Description": "SQL Server ServiceControl persister", "AssemblyName": "ServiceControl.Persistence.EFCore.SqlServer", diff --git a/src/ServiceControl.slnx b/src/ServiceControl.slnx index 5d48bcf3d5..76d72c12f1 100644 --- a/src/ServiceControl.slnx +++ b/src/ServiceControl.slnx @@ -42,7 +42,7 @@ - + From 01a2285e4e19ed2122344dcf2fa37266b7bd54a3 Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 16 Jul 2026 11:58:17 +1000 Subject: [PATCH 4/6] Rename failures --- src/ProjectReferences.Persisters.Primary.props | 2 +- .../PostgreSqlPersistence.cs | 0 .../PostgreSqlPersistenceConfiguration.cs | 0 .../PostgreSqlPersisterSettings.cs | 0 .../PostgreSqlServiceControlDbContext.cs | 0 .../PostgreSqlServiceControlDbContextFactory.cs | 0 .../ServiceControl.Persistence.EFCore.PostgreSql.csproj} | 0 .../persistence.manifest | 0 .../ServiceControl.Persistence.EFCore.SqlServer.csproj | 2 +- .../PrimaryDeploymentPackageTests.cs | 3 +-- 10 files changed, 3 insertions(+), 4 deletions(-) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL => ServiceControl.Persistence.EFCore.PostgreSql}/PostgreSqlPersistence.cs (100%) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL => ServiceControl.Persistence.EFCore.PostgreSql}/PostgreSqlPersistenceConfiguration.cs (100%) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL => ServiceControl.Persistence.EFCore.PostgreSql}/PostgreSqlPersisterSettings.cs (100%) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL => ServiceControl.Persistence.EFCore.PostgreSql}/PostgreSqlServiceControlDbContext.cs (100%) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL => ServiceControl.Persistence.EFCore.PostgreSql}/PostgreSqlServiceControlDbContextFactory.cs (100%) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj => ServiceControl.Persistence.EFCore.PostgreSql/ServiceControl.Persistence.EFCore.PostgreSql.csproj} (100%) rename src/{ServiceControl.Persistence.EFCore.PostgreSQL => ServiceControl.Persistence.EFCore.PostgreSql}/persistence.manifest (100%) diff --git a/src/ProjectReferences.Persisters.Primary.props b/src/ProjectReferences.Persisters.Primary.props index a831563338..de0fbcd871 100644 --- a/src/ProjectReferences.Persisters.Primary.props +++ b/src/ProjectReferences.Persisters.Primary.props @@ -1,7 +1,7 @@ - + diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistence.cs similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistence.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistence.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersistenceConfiguration.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersisterSettings.cs similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlPersisterSettings.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersisterSettings.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContext.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContextFactory.cs similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/PostgreSqlServiceControlDbContextFactory.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContextFactory.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj b/src/ServiceControl.Persistence.EFCore.PostgreSql/ServiceControl.Persistence.EFCore.PostgreSql.csproj similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/ServiceControl.Persistence.EFCore.PostgreSQL.csproj rename to src/ServiceControl.Persistence.EFCore.PostgreSql/ServiceControl.Persistence.EFCore.PostgreSql.csproj diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest b/src/ServiceControl.Persistence.EFCore.PostgreSql/persistence.manifest similarity index 100% rename from src/ServiceControl.Persistence.EFCore.PostgreSQL/persistence.manifest rename to src/ServiceControl.Persistence.EFCore.PostgreSql/persistence.manifest diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj b/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj index db51cbb614..862beed816 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj @@ -31,7 +31,7 @@ - + diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs b/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs index b4d5461092..9460ed9dcf 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs +++ b/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs @@ -15,8 +15,7 @@ public PrimaryDeploymentPackageTests() public void Should_package_storages_individually() { var expectedPersisters = new[] { - "RavenDB35", // Still must exist, as Raven35 persistence.manifest file must be available for SCMU to understand old versions - "RavenDB" + "RavenDB", "SQLServer", "RavenDB35", "PostgreSQL" }; var persisters = deploymentPackage.DeploymentUnits.Where(u => u.Category == "Persisters"); From 0461db0b8bb5ee178a2fcdf8ecac8fb4b81dc11a Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 16 Jul 2026 13:21:50 +1000 Subject: [PATCH 5/6] Add .editorconfig for EFCore persistence projects --- .../.editorconfig | 9 +++++++++ .../.editorconfig | 9 +++++++++ src/ServiceControl.Persistence.EFCore/.editorconfig | 9 +++++++++ ...estTests.ApprovePrimaryInstanceManifests.approved.txt | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig create mode 100644 src/ServiceControl.Persistence.EFCore/.editorconfig diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig b/src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig new file mode 100644 index 0000000000..1a80b3d27b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig @@ -0,0 +1,9 @@ +[*.cs] + +# Justification: ServiceControl app has no synchronization context +dotnet_diagnostic.CA2007.severity = none + +# Disable style rules for auto-generated EF migrations +[Migrations/**.cs] +dotnet_diagnostic.IDE0065.severity = none +generated_code = true \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig b/src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig new file mode 100644 index 0000000000..1a80b3d27b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig @@ -0,0 +1,9 @@ +[*.cs] + +# Justification: ServiceControl app has no synchronization context +dotnet_diagnostic.CA2007.severity = none + +# Disable style rules for auto-generated EF migrations +[Migrations/**.cs] +dotnet_diagnostic.IDE0065.severity = none +generated_code = true \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore/.editorconfig b/src/ServiceControl.Persistence.EFCore/.editorconfig new file mode 100644 index 0000000000..1a80b3d27b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/.editorconfig @@ -0,0 +1,9 @@ +[*.cs] + +# Justification: ServiceControl app has no synchronization context +dotnet_diagnostic.CA2007.severity = none + +# Disable style rules for auto-generated EF migrations +[Migrations/**.cs] +dotnet_diagnostic.IDE0065.severity = none +generated_code = true \ No newline at end of file diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt index cedca1823f..0489c1d713 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt +++ b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt @@ -2,5 +2,5 @@ "PostgreSQL: PostgreSQL", "RavenDB: RavenDB", "RavenDB35: RavenDB 3.5 (Legacy)", - "SqlServer: SqlServer" + "SQLServer: SQL Server" ] \ No newline at end of file From 09a60ee0014e8733587f2e2cea632b16975402d4 Mon Sep 17 00:00:00 2001 From: John Simons Date: Thu, 16 Jul 2026 14:03:37 +1000 Subject: [PATCH 6/6] Warn about incomplete EFCore persistence implementations --- .../PostgreSqlPersistenceConfiguration.cs | 12 ++++++++++-- .../SqlServerPersistenceConfiguration.cs | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs index ad490439af..d6e8705dd3 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs @@ -1,11 +1,19 @@ namespace ServiceControl.Persistence.EFCore.PostgreSql; +using Microsoft.Extensions.Logging; +using ServiceControl.Infrastructure; using ServiceControl.Persistence.EFCore.Abstractions; class PostgreSqlPersistenceConfiguration : EFPersistenceConfigurationBase { - public override IPersistence Create(PersistenceSettings settings) => - new PostgreSqlPersistence((PostgreSqlPersisterSettings)settings); + public override IPersistence Create(PersistenceSettings settings) + { + // Temporary until the persister is fully implemented + LoggerUtil.CreateStaticLogger() + .LogError("The PostgreSQL persistence is still under development and is not ready for use"); + + return new PostgreSqlPersistence((PostgreSqlPersisterSettings)settings); + } protected override EFPersisterSettings CreateSettings(string connectionString) => new PostgreSqlPersisterSettings { ConnectionString = connectionString }; diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs index dc9f3c0f3d..49d5d8e415 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs @@ -1,11 +1,19 @@ namespace ServiceControl.Persistence.EFCore.SqlServer; +using Microsoft.Extensions.Logging; +using ServiceControl.Infrastructure; using ServiceControl.Persistence.EFCore.Abstractions; class SqlServerPersistenceConfiguration : EFPersistenceConfigurationBase { - public override IPersistence Create(PersistenceSettings settings) => - new SqlServerPersistence((SqlServerPersisterSettings)settings); + public override IPersistence Create(PersistenceSettings settings) + { + // Temporary until the persister is fully implemented + LoggerUtil.CreateStaticLogger() + .LogError("The SQL Server persistence is still under development and is not ready for use"); + + return new SqlServerPersistence((SqlServerPersisterSettings)settings); + } protected override EFPersisterSettings CreateSettings(string connectionString) => new SqlServerPersisterSettings { ConnectionString = connectionString };