Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ public static IResourceBuilder<DbGateContainerResource> WithDataBindMount(this I
/// Adds a DbGate container resource to the application.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency. Optional; defaults to <c>dbgate</c>.</param>
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML documentation for the name parameter is inconsistent with the actual behavior. The documentation states the parameter is "Optional; defaults to dbgate", but it's actually a required parameter in the sense that it must be explicitly provided if you want to specify the port (due to parameter ordering).

Consider either:

  1. Adding an overload that takes only int port to make the name truly optional when specifying a port
  2. Updating the documentation to clarify that when providing a port, the name must be specified using named parameters: builder.AddDbGate(port: 9090)

Additionally, given the default value and the [ResourceName] attribute, the ArgumentNullException.ThrowIfNull(name) check at line 69 is unnecessary since the parameter can never be null.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter is mandatory, but the argument is optional on invocation.
It's false that the parameter can never be null.

/// <param name="port">The host port to bind the underlying container to.</param>
/// <remarks>
/// Multiple <see cref="AddDbGate(IDistributedApplicationBuilder, string, int?)"/> calls will return the same resource builder instance.
/// </remarks>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<DbGateContainerResource> AddDbGate(this IDistributedApplicationBuilder builder, [ResourceName] string name, int? port = null)
public static IResourceBuilder<DbGateContainerResource> AddDbGate(this IDistributedApplicationBuilder builder, [ResourceName] string name = "dbgate", int? port = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(name);
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ArgumentNullException.ThrowIfNull(name) check is unnecessary because the name parameter has a default value of "dbgate" and can never be null. This validation should be removed.

Suggested change
ArgumentNullException.ThrowIfNull(name);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does not prevent a null value to be sent.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Aspire.Hosting.ApplicationModel;
using System.Diagnostics.Metrics;
using System.Text;

namespace Aspire.Hosting;
Expand Down Expand Up @@ -37,57 +38,45 @@ public static IResourceBuilder<MongoDBServerResource> WithDbGate(this IResourceB
{
ArgumentNullException.ThrowIfNull(builder);

containerName ??= $"{builder.Resource.Name}-dbgate";
containerName ??= "dbgate";

var dbGateBuilder = DbGateBuilderExtensions.AddDbGate(builder.ApplicationBuilder, containerName);
var dbGateBuilder = builder.ApplicationBuilder.AddDbGate(containerName);

dbGateBuilder
.WithEnvironment(context => ConfigureDbGateContainer(context, builder.ApplicationBuilder));
.WithEnvironment(context => ConfigureDbGateContainer(context, builder))
.WaitFor(builder);

configureContainer?.Invoke(dbGateBuilder);

return builder;
}

private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IResourceBuilder<MongoDBServerResource> builder)
{
var mongoDBInstances = applicationBuilder.Resources.OfType<MongoDBServerResource>();
var mongoDBServer = builder.Resource;

var counter = 1;
var name = mongoDBServer.Name;
var label = $"LABEL_{name}";

// Multiple WithDbGate calls will be ignored
if (context.EnvironmentVariables.ContainsKey($"LABEL_mongodb{counter}"))
if (context.EnvironmentVariables.ContainsKey(label))
{
return;
}

foreach (var mongoDBServer in mongoDBInstances)
{
// DbGate assumes MongoDB is being accessed over a default Aspire container network and hardcodes the resource address
// This will need to be refactored once updated service discovery APIs are available
context.EnvironmentVariables.Add($"LABEL_mongodb{counter}", mongoDBServer.Name);
context.EnvironmentVariables.Add($"URL_mongodb{counter}", mongoDBServer.ConnectionStringExpression);
context.EnvironmentVariables.Add($"ENGINE_mongodb{counter}", "mongo@dbgate-plugin-mongo");
// DbGate assumes MongoDB is being accessed over a default Aspire container network and hardcodes the resource address
// This will need to be refactored once updated service discovery APIs are available
context.EnvironmentVariables.Add(label, name);
context.EnvironmentVariables.Add($"URL_{name}", mongoDBServer.ConnectionStringExpression);
context.EnvironmentVariables.Add($"ENGINE_{name}", "mongo@dbgate-plugin-mongo");

counter++;
if (context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS") is string { Length: > 0 } connections)
{
context.EnvironmentVariables["CONNECTIONS"] = $"{connections},{name}";
}

var instancesCount = mongoDBInstances.Count();
if (instancesCount > 0)
else
{
var strBuilder = new StringBuilder();
strBuilder.AppendJoin(',', Enumerable.Range(1, instancesCount).Select(i => $"mongodb{i}"));
var connections = strBuilder.ToString();

string CONNECTIONS = context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS")?.ToString() ?? string.Empty;
if (string.IsNullOrEmpty(CONNECTIONS))
{
context.EnvironmentVariables["CONNECTIONS"] = connections;
}
else
{
context.EnvironmentVariables["CONNECTIONS"] += $",{connections}";
}
context.EnvironmentVariables["CONNECTIONS"] = name;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,59 +79,47 @@ public static IResourceBuilder<MySqlServerResource> WithDbGate(this IResourceBui
{
ArgumentNullException.ThrowIfNull(builder);

containerName ??= $"{builder.Resource.Name}-dbgate";
containerName ??= "dbgate";

var dbGateBuilder = DbGateBuilderExtensions.AddDbGate(builder.ApplicationBuilder, containerName);

dbGateBuilder
.WithEnvironment(context => ConfigureDbGateContainer(context, builder.ApplicationBuilder));
.WithEnvironment(context => ConfigureDbGateContainer(context, builder))
.WaitFor(builder);

configureContainer?.Invoke(dbGateBuilder);

return builder;
}

private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IResourceBuilder<MySqlServerResource> builder)
{
var mysqlInstances = applicationBuilder.Resources.OfType<MySqlServerResource>();
var mySqlServer = builder.Resource;

var counter = 1;
var name = mySqlServer.Name;
var label = $"LABEL_{name}";

// Multiple WithDbGate calls will be ignored
if (context.EnvironmentVariables.ContainsKey($"LABEL_mysql{counter}"))
if (context.EnvironmentVariables.ContainsKey(label))
{
return;
}

foreach (var mySqlServerResource in mysqlInstances)
// DbGate assumes MySql is being accessed over a default Aspire container network and hardcodes the resource address
context.EnvironmentVariables.Add(label, name);
context.EnvironmentVariables.Add($"SERVER_{name}", name);
context.EnvironmentVariables.Add($"USER_{name}", "root");
context.EnvironmentVariables.Add($"PASSWORD_{name}", mySqlServer.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_{name}", mySqlServer.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_{name}", "mysql@dbgate-plugin-mysql");

if (context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS") is string { Length: > 0 } connections)
{
// DbGate assumes MySql is being accessed over a default Aspire container network and hardcodes the resource address
context.EnvironmentVariables.Add($"LABEL_mysql{counter}", mySqlServerResource.Name);
context.EnvironmentVariables.Add($"SERVER_mysql{counter}", mySqlServerResource.Name);
context.EnvironmentVariables.Add($"USER_mysql{counter}", "root");
context.EnvironmentVariables.Add($"PASSWORD_mysql{counter}", mySqlServerResource.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_mysql{counter}", mySqlServerResource.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_mysql{counter}", "mysql@dbgate-plugin-mysql");

counter++;
context.EnvironmentVariables["CONNECTIONS"] = $"{connections},{name}";
}

var instancesCount = mysqlInstances.Count();
if (instancesCount > 0)
else
{
var strBuilder = new StringBuilder();
strBuilder.AppendJoin(',', Enumerable.Range(1, instancesCount).Select(i => $"mysql{i}"));
var connections = strBuilder.ToString();

string CONNECTIONS = context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS")?.ToString() ?? string.Empty;
if (string.IsNullOrEmpty(CONNECTIONS))
{
context.EnvironmentVariables["CONNECTIONS"] = connections;
}
else
{
context.EnvironmentVariables["CONNECTIONS"] += $",{connections}";
}
context.EnvironmentVariables["CONNECTIONS"] = name;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ public static IResourceBuilder<PostgresServerResource> WithDbGate(this IResource
{
ArgumentNullException.ThrowIfNull(builder);

containerName ??= $"{builder.Resource.Name}-dbgate";
containerName ??= "dbgate";

var dbGateBuilder = DbGateBuilderExtensions.AddDbGate(builder.ApplicationBuilder, containerName);

dbGateBuilder
.WithEnvironment(context => ConfigureDbGateContainer(context, builder.ApplicationBuilder));
.WithEnvironment(context => ConfigureDbGateContainer(context, builder))
.WaitFor(builder);

configureContainer?.Invoke(dbGateBuilder);

Expand Down Expand Up @@ -91,52 +92,39 @@ public static IResourceBuilder<PostgresServerResource> WithAdminer(this IResourc
return builder;
}

private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IResourceBuilder<PostgresServerResource> builder)
{
var postgresInstances = applicationBuilder.Resources.OfType<PostgresServerResource>();
var postgresServer = builder.Resource;

var counter = 1;
var name = postgresServer.Name;
var label = $"LABEL_{name}";

// Multiple WithDbGate calls will be ignored
if (context.EnvironmentVariables.ContainsKey($"LABEL_postgres{counter}"))
if (context.EnvironmentVariables.ContainsKey(label))
{
return;
}

foreach (var postgresServer in postgresInstances)
var userParameter = postgresServer.UserNameParameter is null
? ReferenceExpression.Create($"postgres")
: ReferenceExpression.Create($"{postgresServer.UserNameParameter}");

// DbGate assumes Postgres is being accessed over a default Aspire container network and hardcodes the resource address
// This will need to be refactored once updated service discovery APIs are available
context.EnvironmentVariables.Add($"LABEL_{name}", postgresServer.Name);
context.EnvironmentVariables.Add($"SERVER_{name}", postgresServer.Name);
context.EnvironmentVariables.Add($"USER_{name}", userParameter);
context.EnvironmentVariables.Add($"PASSWORD_{name}", postgresServer.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_{name}", postgresServer.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_{name}", "postgres@dbgate-plugin-postgres");

if (context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS") is string { Length: > 0 } connections)
{
var userParameter = postgresServer.UserNameParameter is null
? ReferenceExpression.Create($"postgres")
: ReferenceExpression.Create($"{postgresServer.UserNameParameter}");

// DbGate assumes Postgres is being accessed over a default Aspire container network and hardcodes the resource address
// This will need to be refactored once updated service discovery APIs are available
context.EnvironmentVariables.Add($"LABEL_postgres{counter}", postgresServer.Name);
context.EnvironmentVariables.Add($"SERVER_postgres{counter}", postgresServer.Name);
context.EnvironmentVariables.Add($"USER_postgres{counter}", userParameter);
context.EnvironmentVariables.Add($"PASSWORD_postgres{counter}", postgresServer.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_postgres{counter}", postgresServer.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_postgres{counter}", "postgres@dbgate-plugin-postgres");

counter++;
context.EnvironmentVariables["CONNECTIONS"] = $"{connections},{name}";
}

var instancesCount = postgresInstances.Count();
if (instancesCount > 0)
else
{
var strBuilder = new StringBuilder();
strBuilder.AppendJoin(',', Enumerable.Range(1, instancesCount).Select(i => $"postgres{i}"));
var connections = strBuilder.ToString();

string CONNECTIONS = context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS")?.ToString() ?? string.Empty;
if (string.IsNullOrEmpty(CONNECTIONS))
{
context.EnvironmentVariables["CONNECTIONS"] = connections;
}
else
{
context.EnvironmentVariables["CONNECTIONS"] += $",{connections}";
}
context.EnvironmentVariables["CONNECTIONS"] = name;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,61 +36,42 @@ public static IResourceBuilder<RedisResource> WithDbGate(this IResourceBuilder<R
{
ArgumentNullException.ThrowIfNull(builder);

containerName ??= $"{builder.Resource.Name}-dbgate";
containerName ??= "dbgate";

var dbGateBuilder = DbGateBuilderExtensions.AddDbGate(builder.ApplicationBuilder, containerName);
var dbGateBuilder = builder.ApplicationBuilder.AddDbGate(containerName);

dbGateBuilder
.WithEnvironment(context => ConfigureDbGateContainer(context, builder.ApplicationBuilder));
.WithEnvironment(context => ConfigureDbGateContainer(context, builder))
.WaitFor(builder);

configureContainer?.Invoke(dbGateBuilder);

return builder;
}

private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IResourceBuilder<RedisResource> builder)
{
var reidsInstances = applicationBuilder.Resources.OfType<RedisResource>();
var redisResource = builder.Resource;

var counter = 1;
var name = redisResource.Name;
var lalbel = $"LABEL_{name}";

// Multiple WithDbGate calls will be ignored
if (context.EnvironmentVariables.ContainsKey($"LABEL_redis{counter}"))
{
return;
}

foreach (var redisResource in reidsInstances)
{
// DbGate assumes Redis is being accessed over a default Aspire container network and hardcodes the resource address
var redisUrl = redisResource.PasswordParameter is not null ?
ReferenceExpression.Create($"redis://:{redisResource.PasswordParameter}@{name}:{redisResource.PrimaryEndpoint.TargetPort?.ToString()}") :
ReferenceExpression.Create($"redis://{name}:{redisResource.PrimaryEndpoint.TargetPort?.ToString()}");

// DbGate assumes Redis is being accessed over a default Aspire container network and hardcodes the resource address
var redisUrl = redisResource.PasswordParameter is not null ?
ReferenceExpression.Create($"redis://:{redisResource.PasswordParameter}@{redisResource.Name}:{redisResource.PrimaryEndpoint.TargetPort?.ToString()}") :
ReferenceExpression.Create($"redis://{redisResource.Name}:{redisResource.PrimaryEndpoint.TargetPort?.ToString()}");
context.EnvironmentVariables.Add(lalbel, name);
context.EnvironmentVariables.Add($"URL_{name}", redisUrl);
context.EnvironmentVariables.Add($"ENGINE_{name}", "redis@dbgate-plugin-redis");

context.EnvironmentVariables.Add($"LABEL_redis{counter}", redisResource.Name);
context.EnvironmentVariables.Add($"URL_redis{counter}", redisUrl);
context.EnvironmentVariables.Add($"ENGINE_redis{counter}", "redis@dbgate-plugin-redis");

counter++;
if (context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS") is string { Length: > 0 } connections)
{
context.EnvironmentVariables["CONNECTIONS"] = $"{connections},{name}";
}

var instancesCount = reidsInstances.Count();
if (instancesCount > 0)
else
{
var strBuilder = new StringBuilder();
strBuilder.AppendJoin(',', Enumerable.Range(1, instancesCount).Select(i => $"redis{i}"));
var connections = strBuilder.ToString();

string CONNECTIONS = context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS")?.ToString() ?? string.Empty;
if (string.IsNullOrEmpty(CONNECTIONS))
{
context.EnvironmentVariables["CONNECTIONS"] = connections;
}
else
{
context.EnvironmentVariables["CONNECTIONS"] += $",{connections}";
}
context.EnvironmentVariables["CONNECTIONS"] = name;
}
}
}
Loading
Loading