diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..b939ffc --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,9 @@ + + + + + 9.0.15 + 10.0.7 + + + diff --git a/TaskTurnstile.Redis/DependencyInjection/ConcurrencyManagerBuilderExtensions.cs b/TaskTurnstile.Redis/DependencyInjection/TaskTurnstileBuilderExtensions.cs similarity index 100% rename from TaskTurnstile.Redis/DependencyInjection/ConcurrencyManagerBuilderExtensions.cs rename to TaskTurnstile.Redis/DependencyInjection/TaskTurnstileBuilderExtensions.cs diff --git a/TaskTurnstile.SqlServer/DependencyInjection/ConcurrencyManagerBuilderExtensions.cs b/TaskTurnstile.SqlServer/DependencyInjection/TaskTurnstileBuilderExtensions.cs similarity index 100% rename from TaskTurnstile.SqlServer/DependencyInjection/ConcurrencyManagerBuilderExtensions.cs rename to TaskTurnstile.SqlServer/DependencyInjection/TaskTurnstileBuilderExtensions.cs diff --git a/TaskTurnstile.SqlServer/SqlServerTableInitializerHostedService.cs b/TaskTurnstile.SqlServer/SqlServerTableInitializerHostedService.cs new file mode 100644 index 0000000..36414e9 --- /dev/null +++ b/TaskTurnstile.SqlServer/SqlServerTableInitializerHostedService.cs @@ -0,0 +1,60 @@ +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Hosting; +using System.Data; + +namespace TaskTurnstile.SqlServer; + +internal sealed class SqlServerTableInitializerHostedService( + string connectionString, + string schemaName, + string tableName) : IHostedService +{ + public async Task StartAsync(CancellationToken cancellationToken) + { + var quotedTable = $"[{schemaName.Replace("]", "]]")}].[{tableName.Replace("]", "]]")}]"; + var escapedSchema = schemaName.Replace("'", "''"); + var escapedTable = tableName.Replace("'", "''"); + + var tableInfoSql = + $"SELECT 1 FROM INFORMATION_SCHEMA.TABLES " + + $"WHERE TABLE_SCHEMA = '{escapedSchema}' AND TABLE_NAME = '{escapedTable}'"; + + var createTableSql = + $"CREATE TABLE {quotedTable}(" + + "Id nvarchar(449) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, " + + "Value varbinary(MAX) NOT NULL, " + + "ExpiresAtTime datetimeoffset NOT NULL, " + + "SlidingExpirationInSeconds bigint NULL, " + + "AbsoluteExpiration datetimeoffset NULL, " + + "PRIMARY KEY (Id))"; + + var createIndexSql = + $"CREATE NONCLUSTERED INDEX Index_ExpiresAtTime ON {quotedTable}(ExpiresAtTime)"; + + await using var connection = new SqlConnection(connectionString); + await connection.OpenAsync(cancellationToken); + + await using var checkCommand = new SqlCommand(tableInfoSql, connection); + await using var reader = await checkCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken); + var exists = await reader.ReadAsync(cancellationToken); + await reader.CloseAsync(); + + if (exists) + return; + + await using var transaction = (SqlTransaction)await connection.BeginTransactionAsync(cancellationToken); + try + { + await new SqlCommand(createTableSql, connection, transaction).ExecuteNonQueryAsync(cancellationToken); + await new SqlCommand(createIndexSql, connection, transaction).ExecuteNonQueryAsync(cancellationToken); + await transaction.CommitAsync(cancellationToken); + } + catch + { + await transaction.RollbackAsync(cancellationToken); + throw; + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} diff --git a/TaskTurnstile/DependencyInjection/TaskControlTowerBuilder.cs b/TaskTurnstile/DependencyInjection/TaskTurnstileBuilder.cs similarity index 97% rename from TaskTurnstile/DependencyInjection/TaskControlTowerBuilder.cs rename to TaskTurnstile/DependencyInjection/TaskTurnstileBuilder.cs index 7b21c90..9aaf794 100644 --- a/TaskTurnstile/DependencyInjection/TaskControlTowerBuilder.cs +++ b/TaskTurnstile/DependencyInjection/TaskTurnstileBuilder.cs @@ -1,7 +1,6 @@ using TaskTurnstile.Stores; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace TaskTurnstile.DependencyInjection;