Skip to content

Commit dd71d99

Browse files
Copilotardalis
andcommitted
Fix running clean architecture template on Linux
- Skip LocalDB connection on non-Windows platforms in InfrastructureServiceExtensions - Use EnsureCreated for SQLite, Migrate for SQL Server in MiddlewareConfig - Update appsettings.json comments to clarify cross-platform behavior - Update functional tests to force SQL Server mode on Linux via environment variable Co-authored-by: ardalis <[email protected]>
1 parent 473bb94 commit dd71d99

File tree

4 files changed

+39
-8
lines changed

4 files changed

+39
-8
lines changed

src/Clean.Architecture.Infrastructure/InfrastructureServiceExtensions.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ public static IServiceCollection AddInfrastructureServices(
1414
{
1515
// Try to get connection strings in order of priority:
1616
// 1. "cleanarchitecture" - provided by Aspire when using .WithReference(cleanArchDb)
17-
// 2. "DefaultConnection" - traditional SQL Server connection
17+
// 2. "DefaultConnection" - SQL Server (Windows only by default, can be forced with USE_SQL_SERVER=true)
1818
// 3. "SqliteConnection" - fallback to SQLite
19+
bool isWindows = OperatingSystem.IsWindows();
20+
bool forceSqlServer = Environment.GetEnvironmentVariable("USE_SQL_SERVER") == "true";
21+
1922
string? connectionString = config.GetConnectionString("cleanarchitecture")
20-
?? config.GetConnectionString("DefaultConnection")
23+
?? ((isWindows || forceSqlServer) ? config.GetConnectionString("DefaultConnection") : null)
2124
?? config.GetConnectionString("SqliteConnection");
2225
Guard.Against.Null(connectionString);
2326

@@ -28,9 +31,9 @@ public static IServiceCollection AddInfrastructureServices(
2831
{
2932
var eventDispatchInterceptor = provider.GetRequiredService<EventDispatchInterceptor>();
3033

31-
// Use SQL Server if Aspire or DefaultConnection is available, otherwise use SQLite
34+
// Use SQL Server if Aspire or DefaultConnection (on Windows or forced) is available, otherwise use SQLite
3235
if (config.GetConnectionString("cleanarchitecture") != null ||
33-
config.GetConnectionString("DefaultConnection") != null)
36+
((isWindows || forceSqlServer) && config.GetConnectionString("DefaultConnection") != null))
3437
{
3538
options.UseSqlServer(connectionString);
3639
}

src/Clean.Architecture.Web/Configurations/MiddlewareConfig.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,19 @@ static async Task MigrateDatabaseAsync(WebApplication app)
5555
{
5656
logger.LogInformation("Applying database migrations...");
5757
var context = services.GetRequiredService<AppDbContext>();
58-
await context.Database.MigrateAsync();
59-
logger.LogInformation("Database migrations applied successfully");
58+
59+
// For SQLite, use EnsureCreated instead of migrations (common for dev/local scenarios)
60+
// For SQL Server, use migrations (production scenario)
61+
if (context.Database.IsSqlite())
62+
{
63+
await context.Database.EnsureCreatedAsync();
64+
logger.LogInformation("SQLite database created successfully");
65+
}
66+
else
67+
{
68+
await context.Database.MigrateAsync();
69+
logger.LogInformation("Database migrations applied successfully");
70+
}
6071
}
6172
catch (Exception ex)
6273
{

src/Clean.Architecture.Web/appsettings.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"ConnectionStrings": {
33
// When running through Aspire, the connection string is provided automatically by the AspireHost
4-
// This DefaultConnection is used only when running the Web project directly (without Aspire)
4+
// This DefaultConnection is used only when running the Web project directly on Windows (without Aspire)
55
// You can configure this to point to your local SQL Server instance
6+
// Note: On Linux/macOS, this connection is skipped and SQLite is used instead
67
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=cleanarchitecture;Trusted_Connection=True;MultipleActiveResultSets=true",
7-
// SQLite is used as a fallback when running standalone without Aspire and no DefaultConnection is configured
8+
// SQLite is used as a fallback when running on Linux/macOS or when running standalone on Windows without Aspire
89
"SqliteConnection": "Data Source=database.sqlite"
910
},
1011
"Database": {

tests/Clean.Architecture.FunctionalTests/CustomWebApplicationFactory.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Clean.Architecture.Infrastructure.Data;
22
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.Extensions.Configuration;
34
using Testcontainers.MsSql;
45

56
namespace Clean.Architecture.FunctionalTests;
@@ -18,6 +19,8 @@ public async Task InitializeAsync()
1819

1920
public new async Task DisposeAsync()
2021
{
22+
// Clean up environment variable
23+
Environment.SetEnvironmentVariable("USE_SQL_SERVER", null);
2124
await _dbContainer.DisposeAsync();
2225
}
2326

@@ -66,7 +69,18 @@ protected override IHost CreateHost(IHostBuilder builder)
6669

6770
protected override void ConfigureWebHost(IWebHostBuilder builder)
6871
{
72+
// Force SQL Server mode even on non-Windows platforms for functional tests
73+
Environment.SetEnvironmentVariable("USE_SQL_SERVER", "true");
74+
6975
builder
76+
.ConfigureAppConfiguration((context, config) =>
77+
{
78+
// Set the connection string to use the Testcontainer
79+
config.AddInMemoryCollection(new Dictionary<string, string?>
80+
{
81+
["ConnectionStrings:DefaultConnection"] = _dbContainer.GetConnectionString()
82+
});
83+
})
7084
.ConfigureServices(services =>
7185
{
7286
// Remove the app's ApplicationDbContext registration
@@ -84,6 +98,8 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
8498
services.AddDbContext<AppDbContext>((provider, options) =>
8599
{
86100
options.UseSqlServer(_dbContainer.GetConnectionString());
101+
var interceptor = provider.GetRequiredService<EventDispatchInterceptor>();
102+
options.AddInterceptors(interceptor);
87103
});
88104
});
89105
}

0 commit comments

Comments
 (0)