aspire icon indicating copy to clipboard operation
aspire copied to clipboard

Add interfaces to PostgreSQL, Redis, and SqlServer Hosting Resources

Open eerhardt opened this issue 1 year ago • 2 comments

Background and Motivation

See https://github.com/dotnet/aspire/pull/5930#issuecomment-2381917879 for some of the context.

With

We re-designed how resources that can be hosted in either a container or a managed Azure resource (ex. PostgreSQL, Redis, SqlServer) are added to the model.

Before

var redis = builder.AddRedis("redis")
                   .PublishAsAzureRedis();

After

var redis = builder.AddAzureRedis("redis")
                   .RunAsContainer();

The downside of this approach is composability of resources. Let's say I wanted to have a resource that depended on a Postgres database instance. With this change the implementor of that would need to have extensions for each cloud provider (unless they just allow any IResourceWithConnectionString).

Proposed API

namespace Aspire.Hosting.Redis;

public interface IRedisResource : IResourceWithConnectionString
{
}

namespace Aspire.Hosting.ApplicationModel;
public class RedisResource : IRedisResource {...} 

namespace Aspire.Hosting.Azure;
public class AzureRedisCacheResource : IRedisResource {...} 

namespace Aspire.Hosting.Postgres;

public interface IPostgresServerResource : IResourceWithConnectionString
{
}

namespace Aspire.Hosting.ApplicationModel;
public class PostgresServerResource : IPostgresServerResource {...}

namespace Aspire.Hosting.Azure;
public class AzurePostgresFlexibleServerResource : IPostgresServerResource {...}

namespace Aspire.Hosting.SqlServer;

public interface ISqlServerServerResource : IResourceWithConnectionString
{
}

namespace Aspire.Hosting.ApplicationModel;
public class SqlServerServerResource : ISqlServerServerResource  {...}

namespace Aspire.Hosting.Azure;
public class AzureSqlServerServerResource : ISqlServerServerResource  {...}

Usage Examples


var redis1 = builder.AddRedis("cache1");
UseRedis(redis1);

var redis2 = builder.AddAzureRedis("cache2");
UseRedis(redis2);

static void UseRedis(IResourceBuilder<IRedisResource> redis)
{
   ... get the connection string, or use the resource some other way
}

Alternative Designs

Risks

cc @davidfowl @mitchdenny

eerhardt avatar Oct 01 '24 19:10 eerhardt

Yep - I think we need to do this otherwise we'll stifle reuse of some of these fundamental application building blocks. You'll end up with packages on NuGet that look like this:

MyAspireStuff.Postgres.Azure MyAspireStuff.Postgres.AWS

... only because there isn't a shared type for the Redis resources.

mitchdenny avatar Oct 02 '24 01:10 mitchdenny

I'm adding it to the backlog for now. I don't think we block 9.0 for it but I think it would be nice to see in 9.x.

mitchdenny avatar Oct 02 '24 01:10 mitchdenny

for sql-server I found the following way:

var sqlServer = builder.AddSqlServer("sql-server")
    .WithDataVolume("data");

sqlServer.WithCommand("open-in-vscode", "Open in VS Code", async context =>
{
    var rawConnectionString = await sqlServer.Resource.GetConnectionStringAsync();
    if (string.IsNullOrEmpty(rawConnectionString))
    {
        return new ExecuteCommandResult
        {
            Success = false,
            ErrorMessage = "Connection string is not available."
        };
    }

    VsCode.OpenSqlServerInVsCode(rawConnectionString);
    return new ExecuteCommandResult { Success = true };
});

and the magic:



using System.Diagnostics;

using Microsoft.Data.SqlClient;

namespace Prevention.SecurityForAI;

public class VsCode
{
    public static void OpenSqlServerInVsCode(string rawConnectionString)
    {
        var connectionString = new SqlConnectionStringBuilder(rawConnectionString);
        string server = Uri.EscapeDataString(connectionString.DataSource);
        string database = Uri.EscapeDataString(connectionString.InitialCatalog);
        string userId = Uri.EscapeDataString(connectionString.UserID);
        string password = Uri.EscapeDataString(connectionString.Password);

        var vscodeUri = new Uri($"vscode://ms-mssql.mssql/connect?server={server}&database={database}&authenticationType=SqlLogin&user={userId}&password={password}&trustServerCertificate=true");
        var process = Process.Start(new ProcessStartInfo
        {
            FileName = vscodeUri.ToString(),
            UseShellExecute = true
        });
    }
}

this requires the https://marketplace.visualstudio.com/items?itemName=ms-mssql.mssql VsCode extension

Image

@eerhardt / @eerhardt / @davidfowl is this out-of-scope here? should I create a separate issue for this? Maybe having a VsCode extention point that can open UI extensions for specific container based resources

Meir017 avatar Jul 07 '25 10:07 Meir017

This issue is about a C# interface not a user interface 😅.

I’ve marked it as off topic, and yea you can file a new issue

davidfowl avatar Jul 08 '25 04:07 davidfowl