Skip to content

Commit fc7fc08

Browse files
committed
feat: add mongodb container and connection support
1 parent 42979ba commit fc7fc08

24 files changed

+1193
-3
lines changed

Aspire.sln

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.MySqlConnector.Tests
160160
EndProject
161161
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProject.IntegrationServiceA", "tests\testproject\TestProject.IntegrationServiceA\TestProject.IntegrationServiceA.csproj", "{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}"
162162
EndProject
163+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.MongoDB.Driver", "src\Components\Aspire.MongoDB.Driver\Aspire.MongoDB.Driver.csproj", "{20A5A907-A135-4735-B4BF-E13514F360E3}"
164+
EndProject
165+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.MongoDB.Driver.Tests", "tests\Aspire.MongoDB.Driver.Tests\Aspire.MongoDB.Driver.Tests.csproj", "{E592E447-BA3C-44FA-86C1-EBEDC864A644}"
166+
EndProject
163167
Global
164168
GlobalSection(SolutionConfigurationPlatforms) = preSolution
165169
Debug|Any CPU = Debug|Any CPU
@@ -430,6 +434,18 @@ Global
430434
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
431435
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
432436
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Release|Any CPU.Build.0 = Release|Any CPU
437+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
438+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
439+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
440+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Release|Any CPU.Build.0 = Release|Any CPU
441+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
442+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Debug|Any CPU.Build.0 = Debug|Any CPU
443+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Release|Any CPU.ActiveCfg = Release|Any CPU
444+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Release|Any CPU.Build.0 = Release|Any CPU
445+
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
446+
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
447+
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
448+
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Release|Any CPU.Build.0 = Release|Any CPU
433449
EndGlobalSection
434450
GlobalSection(SolutionProperties) = preSolution
435451
HideSolutionNode = FALSE
@@ -504,6 +520,8 @@ Global
504520
{6472D59F-7C04-43DE-AD33-9F20BE3804BF} = {975F6F41-B455-451D-A312-098DE4A167B6}
505521
{CA283D7F-EB95-4353-B196-C409965D2B42} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
506522
{C8079F06-304F-49B1-A0C1-45AA3782A923} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
523+
{20A5A907-A135-4735-B4BF-E13514F360E3} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
524+
{E592E447-BA3C-44FA-86C1-EBEDC864A644} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
507525
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8} = {975F6F41-B455-451D-A312-098DE4A167B6}
508526
EndGlobalSection
509527
GlobalSection(ExtensibilityGlobals) = postSolution

Directory.Packages.props

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
</PropertyGroup>
55
<ItemGroup>
66
<!-- Azure SDK for .NET dependencies -->
7+
<PackageVersion Include="AspNetCore.HealthChecks.MongoDb" Version="7.0.0" />
78
<PackageVersion Include="Azure.Data.Tables" Version="12.8.2" />
89
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
910
<PackageVersion Include="Azure.Identity" Version="1.10.4" />
@@ -71,6 +72,8 @@
7172
<PackageVersion Include="JsonSchema.Net" Version="5.4.0" />
7273
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.1.1" />
7374
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.1.0" />
75+
<PackageVersion Include="MongoDB.Driver" Version="2.22.0" />
76+
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="1.3.0" />
7477
<PackageVersion Include="MySqlConnector.DependencyInjection" Version="2.3.1" />
7578
<PackageVersion Include="Npgsql.DependencyInjection" Version="8.0.0" />
7679
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
@@ -99,4 +102,4 @@
99102
<PackageVersion Include="Microsoft.Signed.Wix" Version="1.0.0-v3.14.0.5722" />
100103
<PackageVersion Include="Microsoft.DotNet.Build.Tasks.Installers" Version="8.0.0-beta.23564.4" />
101104
</ItemGroup>
102-
</Project>
105+
</Project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Aspire.Hosting.ApplicationModel;
5+
6+
/// <summary>
7+
/// Represents a MongoDB resource that requires a connection string.
8+
/// </summary>
9+
public interface IMongoDBResource : IResourceWithConnectionString
10+
{
11+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Net.Sockets;
5+
using Aspire.Hosting.ApplicationModel;
6+
using Aspire.Hosting.Publishing;
7+
8+
namespace Aspire.Hosting;
9+
10+
/// <summary>
11+
/// Provides extension methods for adding MongoDB resources to an <see cref="IDistributedApplicationBuilder"/>.
12+
/// </summary>
13+
public static class MongoDBBuilderExtensions
14+
{
15+
private const int DefaultContainerPort = 27017;
16+
private const string PasswordEnvVarName = "MONGO_INITDB_ROOT_PASSWORD";
17+
private const string UserNameEnvVarName = "MONGO_INITDB_ROOT_USERNAME";
18+
19+
/// <summary>
20+
/// Adds a MongoDB container to the application model. The default image is "mongo" and the tag is "latest".
21+
/// </summary>
22+
/// <returns></returns>
23+
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
24+
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
25+
/// <param name="port">The host port for MongoDB.</param>
26+
/// <param name="password">The password for the MongoDB root user. Defaults to a random password.</param>
27+
/// <returns>A reference to the <see cref="IResourceBuilder{MongoDBContainerResource}"/>.</returns>
28+
public static IResourceBuilder<MongoDBContainerResource> AddMongoDBContainer(
29+
this IDistributedApplicationBuilder builder,
30+
string name,
31+
int? port = null,
32+
string? password = null)
33+
{
34+
password ??= Guid.NewGuid().ToString("N");
35+
36+
var mongoDBContainer = new MongoDBContainerResource(name, password);
37+
38+
return builder
39+
.AddResource(mongoDBContainer)
40+
.WithManifestPublishingCallback(WriteMongoDBContainerToManifest)
41+
.WithAnnotation(new ServiceBindingAnnotation(ProtocolType.Tcp, port: port, containerPort: DefaultContainerPort)) // Internal port is always 27017.
42+
.WithAnnotation(new ContainerImageAnnotation { Image = "mongo", Tag = "latest" })
43+
.WithEnvironment(PasswordEnvVarName, () => mongoDBContainer.Password)
44+
.WithEnvironment(UserNameEnvVarName, () => mongoDBContainer.UserName);
45+
}
46+
47+
/// <summary>
48+
/// Adds a MongoDB connection to the application model. Connection strings can also be read from the connection string section of the configuration using the name of the resource.
49+
/// </summary>
50+
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
51+
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
52+
/// <param name="connectionString">The MongoDB connection string (optional).</param>
53+
/// <returns>A reference to the <see cref="IResourceBuilder{MongoDBConnectionResource}"/>.</returns>
54+
public static IResourceBuilder<MongoDBConnectionResource> AddMongoDBConnection(this IDistributedApplicationBuilder builder, string name, string? connectionString = null)
55+
{
56+
var mongoDBConnection = new MongoDBConnectionResource(name, connectionString);
57+
58+
return builder
59+
.AddResource(mongoDBConnection)
60+
.WithManifestPublishingCallback(context => context.WriteMongoDBConnectionToManifest(mongoDBConnection));
61+
}
62+
63+
/// <summary>
64+
/// Adds a MongoDB database to the application model.
65+
/// </summary>
66+
/// <param name="builder">The MongoDB server resource builder.</param>
67+
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
68+
/// <returns>A reference to the <see cref="IResourceBuilder{MongoDBDatabaseResource}"/>.</returns>
69+
public static IResourceBuilder<MongoDBDatabaseResource> AddDatabase(this IResourceBuilder<MongoDBContainerResource> builder, string name)
70+
{
71+
var mongoDBDatabase = new MongoDBDatabaseResource(name, builder.Resource);
72+
73+
return builder.ApplicationBuilder
74+
.AddResource(mongoDBDatabase)
75+
.WithManifestPublishingCallback(context => context.WriteMongoDBDatabaseToManifest(mongoDBDatabase));
76+
}
77+
78+
private static void WriteMongoDBContainerToManifest(this ManifestPublishingContext context)
79+
{
80+
context.Writer.WriteString("type", "mongodb.server.v0");
81+
}
82+
83+
private static void WriteMongoDBConnectionToManifest(this ManifestPublishingContext context, MongoDBConnectionResource mongoDbConnection)
84+
{
85+
context.Writer.WriteString("type", "mongodb.connection.v0");
86+
context.Writer.WriteString("connectionString", mongoDbConnection.GetConnectionString());
87+
}
88+
89+
private static void WriteMongoDBDatabaseToManifest(this ManifestPublishingContext context, MongoDBDatabaseResource mongoDbDatabase)
90+
{
91+
context.Writer.WriteString("type", "mongodb.database.v0");
92+
context.Writer.WriteString("parent", mongoDbDatabase.Parent.Name);
93+
}
94+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Aspire.Hosting.ApplicationModel;
5+
6+
/// <summary>
7+
/// A resource that represents a MongoDB connection.
8+
/// </summary>
9+
/// <param name="name">The name of the resource.</param>
10+
/// <param name="connectionString">The MongoDB connection string.</param>
11+
public class MongoDBConnectionResource(string name, string? connectionString) : Resource(name), IMongoDBResource
12+
{
13+
private readonly string? _connectionString = connectionString;
14+
15+
/// <summary>
16+
/// Gets the connection string for the MongoDB server.
17+
/// </summary>
18+
/// <returns>The specified connection string.</returns>
19+
public string? GetConnectionString() => _connectionString;
20+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Aspire.Hosting.MongoDB;
5+
6+
internal class MongoDBConnectionStringBuilder
7+
{
8+
private const string Scheme = "mongodb";
9+
10+
private string? _server;
11+
private int _port;
12+
private string? _userName;
13+
private string? _password;
14+
15+
public MongoDBConnectionStringBuilder WithServer(string server)
16+
{
17+
ArgumentNullException.ThrowIfNullOrWhiteSpace(server, nameof(server));
18+
19+
_server = server;
20+
21+
return this;
22+
}
23+
24+
public MongoDBConnectionStringBuilder WithPort(int port)
25+
{
26+
_port = port;
27+
28+
return this;
29+
}
30+
31+
public MongoDBConnectionStringBuilder WithUserName(string userName)
32+
{
33+
ArgumentNullException.ThrowIfNullOrWhiteSpace(userName, nameof(userName));
34+
35+
_userName = userName;
36+
37+
return this;
38+
}
39+
40+
public MongoDBConnectionStringBuilder WithPassword(string password)
41+
{
42+
ArgumentNullException.ThrowIfNullOrWhiteSpace(password, nameof(password));
43+
44+
_password = password;
45+
46+
return this;
47+
}
48+
49+
public string Build()
50+
{
51+
var builder = new UriBuilder
52+
{
53+
Scheme = Scheme,
54+
Host = _server,
55+
Port = _port,
56+
UserName = _userName,
57+
Password = _password
58+
};
59+
60+
return builder.ToString();
61+
}
62+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Aspire.Hosting.MongoDB;
5+
6+
namespace Aspire.Hosting.ApplicationModel;
7+
8+
/// <summary>
9+
/// A resource that represents a MongoDB container.
10+
/// </summary>
11+
/// <param name="name">The name of the resource.</param>
12+
/// <param name="password">The MongoDB root password.</param>
13+
public class MongoDBContainerResource(string name, string password) : ContainerResource(name), IMongoDBResource
14+
{
15+
private const string DefaultUserName = "root";
16+
17+
public string Password { get; } = password;
18+
19+
public string UserName { get; } = DefaultUserName;
20+
21+
/// <summary>
22+
/// Gets the connection string for the MongoDB server.
23+
/// </summary>
24+
/// <returns>A connection string for the MongoDB server in the form "mongodb://host:port".</returns>
25+
public string? GetConnectionString()
26+
{
27+
if (!this.TryGetAllocatedEndPoints(out var allocatedEndpoints))
28+
{
29+
throw new DistributedApplicationException("Expected allocated endpoints!");
30+
}
31+
32+
var allocatedEndpoint = allocatedEndpoints.Single();
33+
34+
return new MongoDBConnectionStringBuilder()
35+
.WithServer(allocatedEndpoint.Address)
36+
.WithPort(allocatedEndpoint.Port)
37+
.WithUserName(UserName)
38+
.WithPassword(Password)
39+
.Build();
40+
}
41+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Text;
5+
6+
namespace Aspire.Hosting.ApplicationModel;
7+
8+
/// <summary>
9+
/// A resource that represents a MongoDB database. This is a child resource of a <see cref="MongoDBContainerResource"/>.
10+
/// </summary>
11+
/// <param name="name">The name of the resource.</param>
12+
/// <param name="mongoDBContainer">The MongoDB server resource associated with this database.</param>
13+
public class MongoDBDatabaseResource(string name, MongoDBContainerResource mongoDBContainer)
14+
: Resource(name), IMongoDBResource, IResourceWithParent<MongoDBContainerResource>
15+
{
16+
public MongoDBContainerResource Parent => mongoDBContainer;
17+
18+
/// <summary>
19+
/// Gets the connection string for the MongoDB database.
20+
/// </summary>
21+
/// <returns>A connection string for the MongoDB database.</returns>
22+
public string? GetConnectionString()
23+
{
24+
if (Parent.GetConnectionString() is { } connectionString)
25+
{
26+
var builder = new StringBuilder(connectionString);
27+
28+
if (!connectionString.EndsWith('/'))
29+
{
30+
builder.Append('/');
31+
}
32+
33+
builder.Append(Name);
34+
35+
return builder.ToString();
36+
}
37+
38+
throw new DistributedApplicationException("Parent resource connection string was null.");
39+
}
40+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(NetCurrent)</TargetFramework>
5+
<IsPackable>true</IsPackable>
6+
<PackageTags>$(ComponentDatabasePackageTags) MongoDB</PackageTags>
7+
<Description>A generic MongoDB client that integrates with Aspire.</Description>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<Compile Include="..\Common\HealthChecksExtensions.cs" Link="HealthChecksExtensions.cs" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" />
16+
<PackageReference Include="MongoDB.Driver" />
17+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
18+
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
19+
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
20+
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" />
21+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
22+
</ItemGroup>
23+
24+
25+
</Project>

0 commit comments

Comments
 (0)