diff --git a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.csproj b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.csproj
index 625db718c..2ab3cad52 100644
--- a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.csproj
+++ b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.csproj
@@ -15,6 +15,7 @@
+
diff --git a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/Program.cs b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/Program.cs
index 325c7d91c..c0f47d0d1 100644
--- a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/Program.cs
+++ b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost/Program.cs
@@ -10,4 +10,14 @@
postgres2.AddDatabase("db3");
postgres2.AddDatabase("db4");
+var sqlserver1 = builder.AddSqlServer("sqlserver1")
+ .WithAdminer();
+sqlserver1.AddDatabase("db5");
+sqlserver1.AddDatabase("db6");
+
+var sqlserver2 = builder.AddSqlServer("sqlserver2")
+ .WithAdminer();
+sqlserver2.AddDatabase("db7");
+sqlserver2.AddDatabase("db8");
+
builder.Build().Run();
\ No newline at end of file
diff --git a/examples/sqlserver-ext/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.AppHost/Program.cs b/examples/sqlserver-ext/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.AppHost/Program.cs
index c2f41067e..eb697ec13 100644
--- a/examples/sqlserver-ext/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.AppHost/Program.cs
+++ b/examples/sqlserver-ext/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.AppHost/Program.cs
@@ -1,12 +1,14 @@
var builder = DistributedApplication.CreateBuilder(args);
var sqlserver1 = builder.AddSqlServer("sqlserver1")
- .WithDbGate(c => c.WithHostPort(8068));
+ .WithDbGate(c => c.WithHostPort(8068))
+ .WithAdminer(c => c.WithHostPort(8069));
sqlserver1.AddDatabase("db1");
sqlserver1.AddDatabase("db2");
var sqlserver2 = builder.AddSqlServer("sqlserver2")
- .WithDbGate();
+ .WithDbGate()
+ .WithAdminer();
sqlserver2.AddDatabase("db3");
sqlserver2.AddDatabase("db4");
diff --git a/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.csproj b/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.csproj
index 79488bab6..4d6914c83 100644
--- a/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.csproj
+++ b/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.csproj
@@ -11,9 +11,14 @@
+
+
+
+
+
diff --git a/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/README.md b/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/README.md
index d20d1e8bc..73f1ac76c 100644
--- a/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/README.md
+++ b/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/README.md
@@ -2,7 +2,7 @@
This integration contains extensions for the [SqlServer hosting package](https://nuget.org/packages/Aspire.Hosting.SqlServer) for .NET Aspire.
-The integration provides support for running [DbGate](https://github.com/dbgate/dbgate) to interact with the SqlServer database.
+The integration provides support for running [DbGate](https://github.com/dbgate/dbgate) and [Adminer](https://github.com/vrana/adminer) to interact with the SqlServer database.
## Getting Started
@@ -20,7 +20,8 @@ Then, in the _Program.cs_ file of `AppHost`, define an SqlServer resource, then
```csharp
var sqlserver = builder.AddSqlServer("sqlserver")
- .WithDbGate();
+ .WithDbGate()
+ .WithAdminer();
```
## Additional Information
diff --git a/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/SqlServerBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/SqlServerBuilderExtensions.cs
index e3f351777..56fd49adc 100644
--- a/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/SqlServerBuilderExtensions.cs
+++ b/src/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions/SqlServerBuilderExtensions.cs
@@ -1,5 +1,6 @@
using Aspire.Hosting.ApplicationModel;
using System.Text;
+using System.Text.Json;
namespace Aspire.Hosting;
@@ -49,6 +50,46 @@ public static IResourceBuilder WithDbGate(this IResourc
return builder;
}
+ ///
+ /// Adds an administration and development platform for SqlServer to the application model using Adminer.
+ ///
+ ///
+ /// This version of the package defaults to the tag of the container image.
+ /// The SqlServer server resource builder.
+ /// Configuration callback for Adminer container resource.
+ /// The name of the container (Optional).
+ ///
+ /// Use in application host with a SqlServer resource
+ ///
+ /// var builder = DistributedApplication.CreateBuilder(args);
+ ///
+ /// var sqlserver = builder.AddSqlServer("sqlserver")
+ /// .WithAdminer();
+ /// var db = sqlserver.AddDatabase("db");
+ ///
+ /// var api = builder.AddProject<Projects.Api>("api")
+ /// .WithReference(db);
+ ///
+ /// builder.Build().Run();
+ ///
+ ///
+ ///
+ /// A reference to the .
+ public static IResourceBuilder WithAdminer(this IResourceBuilder builder, Action>? configureContainer = null, string? containerName = null)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ containerName ??= $"{builder.Resource.Name}-adminer";
+ var adminerBuilder = AdminerBuilderExtensions.AddAdminer(builder.ApplicationBuilder, containerName);
+
+ adminerBuilder
+ .WithEnvironment(context => ConfigureAdminerContainer(context, builder.ApplicationBuilder));
+
+ configureContainer?.Invoke(adminerBuilder);
+
+ return builder;
+ }
+
private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
{
var sqlServerInstances = applicationBuilder.Resources.OfType();
@@ -93,4 +134,45 @@ private static void ConfigureDbGateContainer(EnvironmentCallbackContext context,
}
}
}
+
+ private static void ConfigureAdminerContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
+ {
+ var sqlServerInstances = applicationBuilder.Resources.OfType();
+
+ string ADMINER_SERVERS = context.EnvironmentVariables.GetValueOrDefault("ADMINER_SERVERS")?.ToString() ?? string.Empty;
+
+ var new_servers = sqlServerInstances.ToDictionary(
+ sqlServerServerResource => sqlServerServerResource.Name,
+ sqlServerServerResource =>
+ {
+ return new AdminerLoginServer
+ {
+ Server = sqlServerServerResource.Name,
+ UserName = "sa",
+ Password = sqlServerServerResource.PasswordParameter.Value,
+ Driver = "mssql"
+ };
+ });
+
+ if (string.IsNullOrEmpty(ADMINER_SERVERS))
+ {
+ string servers_json = JsonSerializer.Serialize(new_servers);
+ context.EnvironmentVariables["ADMINER_SERVERS"] = servers_json;
+ }
+ else
+ {
+ var servers = JsonSerializer.Deserialize>(ADMINER_SERVERS) ?? throw new InvalidOperationException("The servers should not be null. This should never happen.");
+ foreach (var server in new_servers)
+ {
+ if (!servers.ContainsKey(server.Key))
+ {
+ servers!.Add(server.Key, server.Value);
+ }
+ }
+ string servers_json = JsonSerializer.Serialize(servers);
+ context.EnvironmentVariables["ADMINER_SERVERS"] = servers_json;
+ }
+
+ }
+
}
\ No newline at end of file
diff --git a/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/AddAdminerTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/AddAdminerTests.cs
index 5ac142d41..7937e4deb 100644
--- a/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/AddAdminerTests.cs
+++ b/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/AddAdminerTests.cs
@@ -146,6 +146,16 @@ public async Task WithAdminerShouldAddAnnotationsForMultipleDatabaseTypes()
var postgresResource2 = postgresResourceBuilder2.Resource;
+ var sqlserverResourceBuilder1 = builder.AddSqlServer("sqlserver1")
+ .WithAdminer();
+
+ var sqlserverResource1 = sqlserverResourceBuilder1.Resource;
+
+ var sqlserverResourceBuilder2 = builder.AddSqlServer("sqlserver2")
+ .WithAdminer();
+
+ var sqlserverResource2 = sqlserverResourceBuilder2.Resource;
+
using var app = builder.Build();
var appModel = app.Services.GetRequiredService();
@@ -181,6 +191,26 @@ public async Task WithAdminerShouldAddAnnotationsForMultipleDatabaseTypes()
Password = postgresResource2.PasswordParameter.Value,
UserName = postgresResource2.UserNameParameter?.Value ?? "postgres"
}
+ },
+ {
+ "sqlserver1",
+ new AdminerLoginServer
+ {
+ Driver = "mssql",
+ Server = sqlserverResource1.Name,
+ Password = sqlserverResource1.PasswordParameter.Value,
+ UserName = "sa"
+ }
+ },
+ {
+ "sqlserver2",
+ new AdminerLoginServer
+ {
+ Driver = "mssql",
+ Server = sqlserverResource2.Name,
+ Password = sqlserverResource2.PasswordParameter.Value,
+ UserName = "sa"
+ }
}
};
@@ -201,6 +231,12 @@ public void WithAdminerShouldShouldAddOneAdminerResourceForMultipleDatabaseTypes
builder.AddPostgres("postgres2")
.WithAdminer();
+ builder.AddSqlServer("sqlserver1")
+ .WithAdminer();
+
+ builder.AddSqlServer("sqlserver2")
+ .WithAdminer();
+
using var app = builder.Build();
var appModel = app.Services.GetRequiredService();
diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests.csproj b/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests.csproj
index 287ebec74..a46f31d14 100644
--- a/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests.csproj
+++ b/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests.csproj
@@ -4,4 +4,7 @@
+
+
+
diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/ResourceCreationTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/ResourceCreationTests.cs
index 89b2c3368..6ed2589af 100644
--- a/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/ResourceCreationTests.cs
+++ b/tests/CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests/ResourceCreationTests.cs
@@ -1,4 +1,5 @@
using Aspire.Hosting;
+using System.Text.Json;
namespace CommunityToolkit.Aspire.Hosting.SqlServer.Extensions.Tests;
@@ -212,4 +213,160 @@ public async Task WithDbGateAddsAnnotationsForMultipleSqlServerResource()
Assert.Equal("sqlserver1,sqlserver2", item.Value);
});
}
+
+ [Fact]
+ public async Task WithAdminerAddsAnnotations()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ var sqlServerResourceBuilder = builder.AddSqlServer("sqlserver")
+ .WithAdminer();
+
+ var sqlserverResource = sqlServerResourceBuilder.Resource;
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var adminerResource = appModel.Resources.OfType().SingleOrDefault();
+
+ Assert.NotNull(adminerResource);
+
+ Assert.Equal("sqlserver-adminer", adminerResource.Name);
+
+ var envs = await adminerResource.GetEnvironmentVariableValuesAsync();
+
+ Assert.NotEmpty(envs);
+
+ var servers = new Dictionary
+ {
+ {
+ "sqlserver",
+ new AdminerLoginServer
+ {
+ Driver = "mssql",
+ Server = sqlserverResource.Name,
+ Password = sqlserverResource.PasswordParameter.Value,
+ UserName = "sa"
+ }
+ },
+ };
+
+ var envValue = JsonSerializer.Serialize(servers);
+ var item = Assert.Single(envs);
+ Assert.Equal("ADMINER_SERVERS", item.Key);
+ Assert.Equal(envValue, item.Value);
+ }
+
+ [Fact]
+ public void MultipleWithAdminerCallsAddsOneDbGateResource()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+ builder.AddSqlServer("sqlserver1").WithAdminer();
+ builder.AddSqlServer("sqlserver2").WithAdminer();
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var adminerContainer = appModel.Resources.OfType().SingleOrDefault();
+ Assert.NotNull(adminerContainer);
+
+ Assert.Equal("sqlserver1-adminer", adminerContainer.Name);
+ }
+
+ [Fact]
+ public void WithAdminerShouldChangeAdminerHostPort()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+ var sqlserverResourceBuilder = builder.AddSqlServer("sqlserver")
+ .WithAdminer(c => c.WithHostPort(8068));
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var adminerContainer = appModel.Resources.OfType().SingleOrDefault();
+ Assert.NotNull(adminerContainer);
+
+ var primaryEndpoint = adminerContainer.Annotations.OfType().Single();
+ Assert.Equal(8068, primaryEndpoint.Port);
+ }
+
+ [Fact]
+ public void WithAdminerShouldChangeAdminerContainerImageTag()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+ var sqlserverResourceBuilder = builder.AddSqlServer("sqlserver")
+ .WithAdminer(c => c.WithImageTag("manualTag"));
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var adminerContainer = appModel.Resources.OfType().SingleOrDefault();
+ Assert.NotNull(adminerContainer);
+
+ var containerImageAnnotation = adminerContainer.Annotations.OfType().Single();
+ Assert.Equal("manualTag", containerImageAnnotation.Tag);
+ }
+
+ [Fact]
+ public async Task WithAdminerAddsAnnotationsForMultipleSqlServerResource()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ var sqlserverResourceBuilder1 = builder.AddSqlServer("sqlserver1")
+ .WithAdminer();
+
+ var sqlserverResource1 = sqlserverResourceBuilder1.Resource;
+
+ var sqlserverResourceBuilder2 = builder.AddSqlServer("sqlserver2")
+ .WithDbGate();
+
+ var sqlserverResource2 = sqlserverResourceBuilder2.Resource;
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var adminerContainer = appModel.Resources.OfType().SingleOrDefault();
+
+ Assert.NotNull(adminerContainer);
+
+ Assert.Equal("sqlserver1-adminer", adminerContainer.Name);
+
+ var envs = await adminerContainer.GetEnvironmentVariableValuesAsync();
+
+ Assert.NotEmpty(envs);
+
+ var servers = new Dictionary
+ {
+ {
+ "sqlserver1",
+ new AdminerLoginServer
+ {
+ Driver = "mssql",
+ Server = sqlserverResource1.Name,
+ Password = sqlserverResource1.PasswordParameter.Value,
+ UserName = "sa"
+ }
+ },
+ {
+ "sqlserver2",
+ new AdminerLoginServer
+ {
+ Driver = "mssql",
+ Server = sqlserverResource2.Name,
+ Password = sqlserverResource2.PasswordParameter.Value,
+ UserName = "sa"
+ }
+ }
+ };
+
+ var envValue = JsonSerializer.Serialize(servers);
+ var item = Assert.Single(envs);
+ Assert.Equal("ADMINER_SERVERS", item.Key);
+ Assert.Equal(envValue, item.Value);
+ }
+
}