Skip to content

Commit 4d9b309

Browse files
committed
feat: add mongodb container and connection support
1 parent 69937e2 commit 4d9b309

13 files changed

+371
-1
lines changed

Aspire.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.MySqlConnector.Tests
158158
EndProject
159159
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProject.IntegrationServiceA", "tests\testproject\TestProject.IntegrationServiceA\TestProject.IntegrationServiceA.csproj", "{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}"
160160
EndProject
161+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.MongoDB.Driver", "src\Components\Aspire.MongoDB.Driver\Aspire.MongoDB.Driver.csproj", "{20A5A907-A135-4735-B4BF-E13514F360E3}"
162+
EndProject
163+
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}"
164+
EndProject
161165
Global
162166
GlobalSection(SolutionConfigurationPlatforms) = preSolution
163167
Debug|Any CPU = Debug|Any CPU
@@ -424,6 +428,14 @@ Global
424428
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
425429
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
426430
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Release|Any CPU.Build.0 = Release|Any CPU
431+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
432+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
433+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
434+
{20A5A907-A135-4735-B4BF-E13514F360E3}.Release|Any CPU.Build.0 = Release|Any CPU
435+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
436+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Debug|Any CPU.Build.0 = Debug|Any CPU
437+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Release|Any CPU.ActiveCfg = Release|Any CPU
438+
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Release|Any CPU.Build.0 = Release|Any CPU
427439
EndGlobalSection
428440
GlobalSection(SolutionProperties) = preSolution
429441
HideSolutionNode = FALSE
@@ -497,6 +509,8 @@ Global
497509
{165411FE-755E-4869-A756-F87F455860AC} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
498510
{CA283D7F-EB95-4353-B196-C409965D2B42} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
499511
{C8079F06-304F-49B1-A0C1-45AA3782A923} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
512+
{20A5A907-A135-4735-B4BF-E13514F360E3} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
513+
{E592E447-BA3C-44FA-86C1-EBEDC864A644} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
500514
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8} = {975F6F41-B455-451D-A312-098DE4A167B6}
501515
EndGlobalSection
502516
GlobalSection(ExtensibilityGlobals) = postSolution

Directory.Packages.props

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<PackageVersion Include="JsonSchema.Net" Version="5.3.1" />
7272
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.0.0" />
7373
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.0.0" />
74+
<PackageVersion Include="MongoDB.Driver" Version="2.19.0" />
7475
<PackageVersion Include="MySqlConnector.DependencyInjection" Version="2.3.1" />
7576
<PackageVersion Include="Npgsql.DependencyInjection" Version="8.0.0-rc.2" />
7677
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-rc.2" />
@@ -99,4 +100,4 @@
99100
<PackageVersion Include="Microsoft.Signed.Wix" Version="1.0.0-v3.14.0.5722" />
100101
<PackageVersion Include="Microsoft.DotNet.Build.Tasks.Installers" Version="8.0.0-beta.23564.4" />
101102
</ItemGroup>
102-
</Project>
103+
</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: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 System.Text.Json;
6+
using Aspire.Hosting.ApplicationModel;
7+
8+
namespace Aspire.Hosting.MongoDb;
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 DefaultPassword = "password";
17+
private const string PasswordEnvVarName = "MONGO_INITDB_ROOT_PASSWORD";
18+
19+
public static IResourceBuilder<MongoDbContainerResource> AddMongoDbContainer(
20+
this IDistributedApplicationBuilder builder,
21+
string name,
22+
int port = DefaultContainerPort,
23+
string password = DefaultPassword)
24+
{
25+
var mongoDbContainer = new MongoDbContainerResource(name, password);
26+
27+
return builder
28+
.AddResource(mongoDbContainer)
29+
.WithAnnotation(new ManifestPublishingCallbackAnnotation(WriteMongoDbContainerToManifest))
30+
.WithAnnotation(new ServiceBindingAnnotation(ProtocolType.Tcp, port: port, containerPort: DefaultContainerPort)) // Internal port is always 27017.
31+
.WithAnnotation(new ContainerImageAnnotation { Image = "mongo", Tag = "latest" })
32+
.WithEnvironment(PasswordEnvVarName, () => mongoDbContainer.Password);
33+
}
34+
35+
public static IResourceBuilder<MongoDbConnectionResource> AddMongoDbConnection(this IDistributedApplicationBuilder builder, string name, string? connectionString = null)
36+
{
37+
var mongoDbConnection = new MongoDbConnectionResource(name, connectionString);
38+
39+
return builder
40+
.AddResource(mongoDbConnection)
41+
.WithAnnotation(new ManifestPublishingCallbackAnnotation((json) => WriteMongoDbConnectionToManifest(json, mongoDbConnection)));
42+
}
43+
44+
/// <summary>
45+
/// Adds a MongoDB database to the application model.
46+
/// </summary>
47+
/// <param name="builder">The MongoDB server resource builder.</param>
48+
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
49+
/// <returns>A reference to the <see cref="IResourceBuilder{MongoDbDatabaseResource}"/>.</returns>
50+
public static IResourceBuilder<MongoDbDatabaseResource> AddDatabase(this IResourceBuilder<MongoDbContainerResource> builder, string name)
51+
{
52+
var mongoDbDatabase = new MongoDbDatabaseResource(name, builder.Resource);
53+
54+
return builder.ApplicationBuilder.AddResource(mongoDbDatabase)
55+
.WithAnnotation(new ManifestPublishingCallbackAnnotation(
56+
(json) => WriteMongoDbDatabaseToManifest(json, mongoDbDatabase)));
57+
}
58+
59+
private static void WriteMongoDbContainerToManifest(Utf8JsonWriter jsonWriter)
60+
{
61+
jsonWriter.WriteString("type", "mongodb.server.v0");
62+
}
63+
64+
private static void WriteMongoDbConnectionToManifest(Utf8JsonWriter jsonWriter, MongoDbConnectionResource mongoDbConnection)
65+
{
66+
jsonWriter.WriteString("type", "mongodb.connection.v0");
67+
jsonWriter.WriteString("connectionString", mongoDbConnection.GetConnectionString());
68+
}
69+
70+
private static void WriteMongoDbDatabaseToManifest(Utf8JsonWriter json, MongoDbDatabaseResource mongoDbDatabase)
71+
{
72+
json.WriteString("type", "mongodb.database.v0");
73+
json.WriteString("parent", mongoDbDatabase.Parent.Name);
74+
}
75+
}
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), IMySqlResource
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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 container.
8+
/// </summary>
9+
/// <param name="name">The name of the resource.</param>
10+
/// <param name="password">The MongoDb root password.</param>
11+
public class MongoDbContainerResource(string name, string password) : ContainerResource(name), IMongoDbResource
12+
{
13+
public string Password { get; } = password;
14+
15+
/// <summary>
16+
/// Gets the connection string for the MongoDb server.
17+
/// </summary>
18+
/// <returns>A connection string for the MongoDb server in the form "mongodb://host:port".</returns>
19+
public string? GetConnectionString()
20+
{
21+
if (!this.TryGetAllocatedEndPoints(out var allocatedEndpoints))
22+
{
23+
throw new DistributedApplicationException("Expected allocated endpoints!");
24+
}
25+
26+
var allocatedEndpoint = allocatedEndpoints.Single();
27+
28+
return $"mongodb://root:{Password}@{allocatedEndpoint.Address}:{allocatedEndpoint.Port}";
29+
}
30+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 database. This is a child resource of a <see cref="MongoDbContainerResource"/>.
8+
/// </summary>
9+
/// <param name="name">The name of the resource.</param>
10+
/// <param name="mongoDbContainer">The MongoDb server resource associated with this database.</param>
11+
public class MongoDbDatabaseResource(string name, MongoDbContainerResource mongoDbContainer)
12+
: Resource(name), IMongoDbResource, IResourceWithParent<MongoDbContainerResource>
13+
{
14+
public MongoDbContainerResource Parent => mongoDbContainer;
15+
16+
/// <summary>
17+
/// Gets the connection string for the MongoDb database.
18+
/// </summary>
19+
/// <returns>A connection string for the MongoDb database.</returns>
20+
public string? GetConnectionString()
21+
{
22+
if (Parent.GetConnectionString() is { } connectionString)
23+
{
24+
return $"{connectionString}/{Name}";
25+
}
26+
else
27+
{
28+
throw new DistributedApplicationException("Parent resource connection string was null.");
29+
}
30+
}
31+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
<PackageReference Include="MongoDB.Driver" />
12+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
13+
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
14+
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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.MongoDB.Driver;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using MongoDB.Driver;
8+
9+
namespace Microsoft.Extensions.Hosting;
10+
11+
/// <summary>
12+
/// Extension methods for connecting MongoDB database with MongoDB.Driver client.
13+
/// </summary>
14+
public static class AspireMongoDbDriverExtensions
15+
{
16+
private const string DefaultConfigSectionName = "Aspire:MongoDB";
17+
18+
public static void AddMongoDbDataSource(
19+
this IHostApplicationBuilder builder,
20+
string connectionName,
21+
Action<MongoDbSettings>? configureSettings = null)
22+
=> AddMongoDbDataSource(builder, DefaultConfigSectionName, configureSettings, connectionName, serviceKey: null);
23+
24+
public static void AddKeyedMongoDbDataSource(
25+
this IHostApplicationBuilder builder,
26+
string name,
27+
Action<MongoDbSettings>? configureSettings = null)
28+
{
29+
ArgumentException.ThrowIfNullOrEmpty(name);
30+
31+
AddMongoDbDataSource(builder, $"{DefaultConfigSectionName}:{name}", configureSettings, connectionName: name, serviceKey: name);
32+
}
33+
34+
private static void AddMongoDbDataSource(
35+
IHostApplicationBuilder builder,
36+
string configurationSectionName,
37+
Action<MongoDbSettings>? configureSettings,
38+
string connectionName,
39+
object? serviceKey)
40+
{
41+
ArgumentNullException.ThrowIfNull(builder);
42+
43+
MongoDbSettings settings = new();
44+
45+
builder.Configuration.GetSection(configurationSectionName).Bind(settings);
46+
47+
if (builder.Configuration.GetConnectionString(connectionName) is string connectionString)
48+
{
49+
settings.ConnectionString = connectionString;
50+
}
51+
52+
configureSettings?.Invoke(settings);
53+
54+
builder.RegisterMongoDbServices(settings, serviceKey);
55+
}
56+
57+
private static void RegisterMongoDbServices(this IHostApplicationBuilder builder, MongoDbSettings settings, object? serviceKey)
58+
{
59+
if (serviceKey is null)
60+
{
61+
builder.Services.AddSingleton<IMongoClient>(_ => new MongoClient(settings.ConnectionString));
62+
}
63+
else
64+
{
65+
builder.Services.AddKeyedSingleton<IMongoClient>(serviceKey, (_, __) => new MongoClient(settings.ConnectionString));
66+
}
67+
}
68+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"definitions": {
3+
"logLevel": {
4+
"properties": {
5+
"MongoDB": {
6+
"$ref": "#/definitions/logLevelThreshold"
7+
},
8+
"MongoDB.Driver": {
9+
"$ref": "#/definitions/logLevelThreshold"
10+
}
11+
}
12+
}
13+
},
14+
"properties": {
15+
"Aspire": {
16+
"type": "object",
17+
"properties": {
18+
"MongoDB": {
19+
"type": "object",
20+
"properties": {
21+
"ConnectionString": {
22+
"type": "string",
23+
"description": "Gets or sets the connection string of the MongoDB database to connect to."
24+
}
25+
}
26+
}
27+
}
28+
}
29+
},
30+
"type": "object"
31+
}

0 commit comments

Comments
 (0)