@@ -10,37 +10,73 @@ public async Task InitialiseAsync(CancellationToken ct)
1010 {
1111 logger . LogInformation ( "Initialising database schema" ) ;
1212
13+ // Connect to master first and create the target database if it doesn't exist.
14+ // This removes the need for a manual sqlcmd step during local setup.
15+ var builder = new SqlConnectionStringBuilder ( connectionString ) ;
16+ string databaseName = builder . InitialCatalog ;
17+ builder . InitialCatalog = "master" ;
18+
19+ await using ( var masterConnection = new SqlConnection ( builder . ConnectionString ) )
20+ {
21+ await masterConnection . OpenAsync ( ct ) ;
22+
23+ // DDL identifiers cannot be parameterised; use QuoteIdentifier to escape the name.
24+ // Split into two ADO.NET commands so neither statement needs string interpolation.
25+ bool dbExists ;
26+ await using ( SqlCommand checkCmd = masterConnection . CreateCommand ( ) )
27+ {
28+ checkCmd . CommandText = "SELECT COUNT(1) FROM sys.databases WHERE name = @name" ;
29+ _ = checkCmd . Parameters . AddWithValue ( "@name" , databaseName ) ;
30+ dbExists = Convert . ToInt32 ( await checkCmd . ExecuteScalarAsync ( ct ) ) > 0 ;
31+ }
32+
33+ if ( ! dbExists )
34+ {
35+ // QuoteIdentifier wraps in brackets and escapes ] as ]], so concatenation is safe.
36+ string quotedDb = new SqlCommandBuilder ( ) . QuoteIdentifier ( databaseName ) ;
37+ await using SqlCommand createCmd = masterConnection . CreateCommand ( ) ;
38+ #pragma warning disable DAP242 // not a Dapper call; quotedDb is a safely-escaped SQL identifier
39+ createCmd . CommandText = "CREATE DATABASE " + quotedDb ;
40+ #pragma warning restore DAP242
41+ _ = await createCmd . ExecuteNonQueryAsync ( ct ) ;
42+ }
43+ }
44+
1345 await using var connection = new SqlConnection ( connectionString ) ;
1446 await connection . OpenAsync ( ct ) ;
1547
16- _ = await connection . ExecuteAsync ( new CommandDefinition ( """
17- IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ProcessingState')
18- BEGIN
19- CREATE TABLE ProcessingState (
20- Id INT PRIMARY KEY DEFAULT 1,
21- LastEventId BIGINT NOT NULL DEFAULT 1,
22- UpdatedAtUtc DATETIMEOFFSET NOT NULL DEFAULT SYSDATETIMEOFFSET(),
23- CONSTRAINT CK_SingleRow CHECK (Id = 1)
24- );
25- INSERT INTO ProcessingState (Id, LastEventId) VALUES (1, 1);
26- END
27- """ , cancellationToken : ct ) ) ;
28-
29- _ = await connection . ExecuteAsync ( new CommandDefinition ( """
30- IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ParcelSummary')
31- BEGIN
32- CREATE TABLE ParcelSummary (
33- ParcelId INT PRIMARY KEY,
34- LatestEventId BIGINT NOT NULL,
35- LatestType NVARCHAR(50) NOT NULL,
36- LatestCreatedDateTimeUtc DATETIMEOFFSET NOT NULL,
37- LatestStatusCode NVARCHAR(50) NOT NULL DEFAULT '',
38- LatestRunId NVARCHAR(50) NOT NULL DEFAULT '',
39- PickedUpAtUtc DATETIMEOFFSET NULL,
40- DeliveredAtUtc DATETIMEOFFSET NULL
41- );
42- END
43- """ , cancellationToken : ct ) ) ;
48+ _ = await connection . ExecuteAsync ( new CommandDefinition (
49+ """
50+ IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = 'ProcessingState')
51+ BEGIN
52+ CREATE TABLE ProcessingState (
53+ Id INT PRIMARY KEY DEFAULT 1,
54+ LastEventId BIGINT NOT NULL DEFAULT 1,
55+ UpdatedAtUtc DATETIMEOFFSET NOT NULL DEFAULT SYSDATETIMEOFFSET(),
56+ CONSTRAINT CK_SingleRow CHECK (Id = 1)
57+ );
58+ INSERT INTO ProcessingState (Id, LastEventId) VALUES (1, 1);
59+ END
60+ """ ,
61+ cancellationToken : ct ) ) ;
62+
63+ _ = await connection . ExecuteAsync ( new CommandDefinition (
64+ """
65+ IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = 'ParcelSummary')
66+ BEGIN
67+ CREATE TABLE ParcelSummary (
68+ ParcelId INT PRIMARY KEY,
69+ LatestEventId BIGINT NOT NULL,
70+ LatestType NVARCHAR(50) NOT NULL,
71+ LatestCreatedDateTimeUtc DATETIMEOFFSET NOT NULL,
72+ LatestStatusCode NVARCHAR(50) NOT NULL DEFAULT '',
73+ LatestRunId NVARCHAR(50) NOT NULL DEFAULT '',
74+ PickedUpAtUtc DATETIMEOFFSET NULL,
75+ DeliveredAtUtc DATETIMEOFFSET NULL
76+ );
77+ END
78+ """ ,
79+ cancellationToken : ct ) ) ;
4480
4581 logger . LogInformation ( "Database schema initialised" ) ;
4682 }
0 commit comments