Skip to content

Commit 1151447

Browse files
committed
fix: factory lambda, remove IL3053
1 parent ef27322 commit 1151447

3 files changed

Lines changed: 81 additions & 32 deletions

File tree

src/ScanEventWorker/Infrastructure/Persistence/DatabaseInitialiser.cs

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/ScanEventWorker/Program.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Amazon;
22
using Amazon.SQS;
3+
using Microsoft.Extensions.Options;
34
using ScanEventWorker.Contracts;
45
using ScanEventWorker.Infrastructure.ApiClient;
56
using ScanEventWorker.Infrastructure.Messaging;
@@ -71,11 +72,24 @@
7172
new SqsMessageQueue(sp.GetRequiredService<IAmazonSQS>(), sqsQueueUrl));
7273

7374
// Services
74-
builder.Services.AddSingleton<IScanEventProcessor, ScanEventProcessor>();
75+
builder.Services.AddSingleton<IScanEventProcessor>(sp =>
76+
new ScanEventProcessor(
77+
sp.GetRequiredService<IScanEventRepository>(),
78+
sp.GetRequiredService<ILogger<ScanEventProcessor>>()));
7579

7680
// Workers
77-
builder.Services.AddHostedService<ApiPollerWorker>();
78-
builder.Services.AddHostedService<EventProcessorWorker>();
81+
builder.Services.AddHostedService(sp =>
82+
new ApiPollerWorker(
83+
sp.GetRequiredService<IScanEventApiClient>(),
84+
sp.GetRequiredService<IScanEventRepository>(),
85+
sp.GetRequiredService<IMessageQueue>(),
86+
sp.GetRequiredService<IOptions<ScanEventApiOptions>>(),
87+
sp.GetRequiredService<ILogger<ApiPollerWorker>>()));
88+
builder.Services.AddHostedService(sp =>
89+
new EventProcessorWorker(
90+
sp.GetRequiredService<IMessageQueue>(),
91+
sp.GetRequiredService<IScanEventProcessor>(),
92+
sp.GetRequiredService<ILogger<EventProcessorWorker>>()));
7993

8094
IHost host = builder.Build();
8195

src/ScanEventWorker/ScanEventWorker.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
<UserSecretsId>dotnet-ScanEventWorker-a630243d-36de-431f-b7cb-d29159cfb22e</UserSecretsId>
88
<PublishAot>true</PublishAot>
99
<IsAotCompatible>true</IsAotCompatible>
10-
<NoWarn>$(NoWarn);IL2104;IL3053</NoWarn>
1110
<InterceptorsNamespaces>$(InterceptorsNamespaces);Dapper.AOT</InterceptorsNamespaces>
1211
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
1312
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>

0 commit comments

Comments
 (0)