Skip to content

Commit 02b4a64

Browse files
authored
Update CosmosDB Container sample for NServiceBus 10 (#7604)
* Remove InputLoopService from CosmosDB container sample - Removed InputLoopService.cs file - Moved input loop logic directly to Program.cs using recommended pattern - Uses host.StartAsync(), GetRequiredService<IMessageSession>(), and host.StopAsync() - Sample builds and maintains same functionality * Add containerization support with Azure Cosmos DB integration - Add environment variable support for COSMOS_CONNECTION_STRING with fallback to local emulator - Create Dockerfile with multi-stage build for .NET 9 sample - Add docker-compose.yml for orchestrated testing - Create comprehensive test script (run_sample.sh) with automated validation - Support both containerized and local development workflows - Verify dynamic container mapping and full message flow functionality * Add NServiceBus 10 version (CosmosDB_4) with .NET 10 preview support - Update to NServiceBus 10.0.0-alpha.2 and related packages - Target .NET 10 with preview language features - Update Dockerfile for .NET 10 preview runtime (mcr.microsoft.com/dotnet/sdk:10.0-preview) - Add prerelease.txt marker file - Maintain full containerization and Azure Cosmos DB support - Verified working with dynamic container mapping and complete message flow * Apply C# language feature refactorings to CosmosDB_4 - Use target-typed new expressions for cleaner object initialization - Update ShipOrder, OrderCompleted, CompleteOrder, and StartOrder instantiation - Maintain serialization compatibility for NServiceBus messages - Verified build and functionality remain intact
1 parent 126b12b commit 02b4a64

26 files changed

+987
-107
lines changed

samples/cosmosdb/container/CosmosDB_3/Client/InputLoopService.cs

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Threading.Tasks;
3-
using Client;
43
using Microsoft.Extensions.DependencyInjection;
54
using Microsoft.Extensions.Hosting;
65
using NServiceBus;
@@ -9,27 +8,44 @@ class Program
98
{
109
public static async Task Main(string[] args)
1110
{
12-
await CreateHostBuilder(args).Build().RunAsync();
13-
}
11+
Console.Title = "Client";
12+
13+
var builder = Host.CreateApplicationBuilder(args);
14+
15+
var endpointConfiguration = new EndpointConfiguration("Samples.CosmosDB.Container.Client");
16+
endpointConfiguration.UsePersistence<LearningPersistence>();
17+
endpointConfiguration.UseTransport(new LearningTransport());
18+
endpointConfiguration.UseSerialization<SystemJsonSerializer>();
1419

15-
public static IHostBuilder CreateHostBuilder(string[] args) =>
16-
Host.CreateDefaultBuilder(args)
17-
.ConfigureServices((hostContext, services) =>
18-
{
19-
Console.Title = "Client";
20-
services.AddHostedService<InputLoopService>();
20+
builder.UseNServiceBus(endpointConfiguration);
2121

22-
}).UseNServiceBus(x =>
23-
{
24-
var endpointConfiguration = new EndpointConfiguration("Samples.CosmosDB.Container.Client");
25-
endpointConfiguration.UsePersistence<LearningPersistence>();
26-
endpointConfiguration.UseTransport(new LearningTransport());
27-
endpointConfiguration.UseSerialization<SystemJsonSerializer>();
22+
var host = builder.Build();
23+
await host.StartAsync();
2824

29-
Console.WriteLine("Press 'S' to send a StartOrder message to the server endpoint");
25+
var messageSession = host.Services.GetRequiredService<IMessageSession>();
3026

31-
Console.WriteLine("Press any other key to exit");
32-
return endpointConfiguration;
33-
});
27+
Console.WriteLine("Press 'S' to send a StartOrder message to the server endpoint");
28+
Console.WriteLine("Press any other key to exit");
3429

30+
while (true)
31+
{
32+
var key = Console.ReadKey();
33+
Console.WriteLine();
34+
35+
var orderId = Guid.NewGuid();
36+
var startOrder = new StartOrder
37+
{
38+
OrderId = orderId
39+
};
40+
if (key.Key == ConsoleKey.S)
41+
{
42+
await messageSession.Send("Samples.CosmosDB.Container.Server", startOrder);
43+
Console.WriteLine($"StartOrder Message sent to Server with OrderId {orderId}");
44+
continue;
45+
}
46+
break;
47+
}
48+
49+
await host.StopAsync();
50+
}
3551
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Build and run the CosmosDB Container sample
2+
# Build context should be from the repository root
3+
#
4+
# Build examples:
5+
# podman build -f samples/cosmosdb/container/CosmosDB_3/Dockerfile -t cosmosdb-container-sample .
6+
# docker build -f samples/cosmosdb/container/CosmosDB_3/Dockerfile -t cosmosdb-container-sample .
7+
#
8+
# Run examples:
9+
# podman run -it --rm cosmosdb-container-sample
10+
# docker run -it --rm cosmosdb-container-sample
11+
12+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
13+
14+
# Install expect and process management tools
15+
RUN apt-get update && apt-get install -y \
16+
expect \
17+
procps \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
# Set environment variable for .NET build issues
21+
ENV DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT=true
22+
23+
WORKDIR /src
24+
25+
# Copy build configuration files from repository root
26+
COPY nuget.config .
27+
COPY Directory.Build.props .
28+
COPY BannedSymbols.txt .
29+
30+
# Copy the sample solution and projects
31+
COPY samples/cosmosdb/container/CosmosDB_3/ ./samples/cosmosdb/container/CosmosDB_3/
32+
33+
# Restore packages
34+
RUN cd samples/cosmosdb/container/CosmosDB_3 && dotnet restore --verbosity minimal
35+
36+
# Build all projects
37+
RUN cd samples/cosmosdb/container/CosmosDB_3 && dotnet build --no-restore --verbosity minimal
38+
39+
# Runtime stage
40+
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS runtime
41+
42+
# Install expect and process management tools for runtime
43+
RUN apt-get update && apt-get install -y \
44+
expect \
45+
procps \
46+
curl \
47+
&& rm -rf /var/lib/apt/lists/*
48+
49+
# Set environment variable for .NET runtime issues
50+
ENV DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT=true
51+
52+
WORKDIR /app
53+
54+
# Create .learningtransport directory for LearningTransport
55+
RUN mkdir -p .learningtransport
56+
57+
# Copy built applications
58+
COPY --from=build /src/samples/cosmosdb/container/CosmosDB_3/Client/bin/Debug/net9.0/ ./Client/
59+
COPY --from=build /src/samples/cosmosdb/container/CosmosDB_3/Server/bin/Debug/net9.0/ ./Server/
60+
COPY --from=build /src/samples/cosmosdb/container/CosmosDB_3/SharedMessages/bin/Debug/net9.0/ ./SharedMessages/
61+
62+
# Copy the run script
63+
COPY samples/cosmosdb/container/CosmosDB_3/run_sample.sh ./
64+
RUN chmod +x run_sample.sh
65+
66+
# Default command
67+
CMD ["./run_sample.sh"]

samples/cosmosdb/container/CosmosDB_3/Server/Program.cs

Lines changed: 62 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,68 +15,80 @@ class Program
1515
builder.AddConsole();
1616
}).CreateLogger<Program>();
1717

18-
1918
public static async Task Main(string[] args)
2019
{
20+
Console.Title = "Server";
2121
logger.LogInformation("Starting application...");
22-
var host = CreateHostBuilder(args).Build();
23-
await host.RunAsync();
24-
}
22+
23+
var builder = Host.CreateApplicationBuilder(args);
2524

26-
public static IHostBuilder CreateHostBuilder(string[] args) =>
27-
Host.CreateDefaultBuilder(args)
28-
.ConfigureServices((hostContext, services) =>
29-
{
30-
Console.Title = "Server";
31-
services.AddLogging();
25+
logger.LogInformation("Configuring NServiceBus...");
3226

33-
}).UseNServiceBus(x =>
34-
{
35-
logger.LogInformation("Configuring NServiceBus...");
27+
#region CosmosDBConfig
28+
var endpointConfiguration = new EndpointConfiguration("Samples.CosmosDB.Container.Server");
3629

37-
#region CosmosDBConfig
38-
var endpointConfiguration = new EndpointConfiguration("Samples.CosmosDB.Container.Server");
30+
var persistence = endpointConfiguration.UsePersistence<CosmosPersistence>();
31+
32+
// Get connection string from environment variable, fallback to local emulator
33+
var connection = Environment.GetEnvironmentVariable("COSMOS_CONNECTION_STRING")
34+
?? @"AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
35+
36+
logger.LogInformation($"Using Cosmos DB connection: {(Environment.GetEnvironmentVariable("COSMOS_CONNECTION_STRING") != null ? "Azure service" : "Local emulator")}");
37+
38+
persistence.DatabaseName("Samples.CosmosDB.Container");
39+
var cosmosClient = new CosmosClient(connection);
40+
persistence.CosmosClient(cosmosClient);
41+
persistence.DefaultContainer("OrderSagaData", "/id");
3942

40-
var persistence = endpointConfiguration.UsePersistence<CosmosPersistence>();
41-
var connection = @"AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
42-
persistence.DatabaseName("Samples.CosmosDB.Container");
43-
var cosmosClient = new CosmosClient(connection);
44-
persistence.CosmosClient(cosmosClient);
45-
persistence.DefaultContainer("OrderSagaData", "/id");
43+
#endregion
4644

47-
#endregion
45+
endpointConfiguration.UseTransport(new LearningTransport());
46+
endpointConfiguration.UseSerialization<SystemJsonSerializer>();
47+
endpointConfiguration.EnableInstallers();
4848

49-
endpointConfiguration.UseTransport(new LearningTransport());
50-
endpointConfiguration.UseSerialization<SystemJsonSerializer>();
51-
endpointConfiguration.EnableInstallers();
49+
#region ContainerInformationFromLogicalMessage
50+
var transactionInformation = persistence.TransactionInformation();
51+
transactionInformation.ExtractContainerInformationFromMessage<ShipOrder>(m =>
52+
{
53+
logger.LogInformation($"Message '{m.GetType().AssemblyQualifiedName}' destined to be handled by '{nameof(ShipOrderSaga)}' will use 'ShipOrderSagaData' container.");
54+
return new ContainerInformation("ShipOrderSagaData", new PartitionKeyPath("/id"));
55+
});
56+
#endregion
57+
#region ContainerInformationFromHeaders
58+
transactionInformation.ExtractContainerInformationFromHeaders(headers =>
59+
{
60+
if (headers.TryGetValue(Headers.SagaType, out var sagaTypeHeader) && sagaTypeHeader.Contains(nameof(ShipOrderSaga)))
61+
{
62+
logger.LogInformation($"Message '{headers[Headers.EnclosedMessageTypes]}' destined to be handled by '{nameof(ShipOrderSaga)}' will use 'ShipOrderSagaData' container.");
5263

53-
#region ContainerInformationFromLogicalMessage
54-
var transactionInformation = persistence.TransactionInformation();
55-
transactionInformation.ExtractContainerInformationFromMessage<ShipOrder>(m =>
56-
{
57-
logger.LogInformation($"Message '{m.GetType().AssemblyQualifiedName}' destined to be handled by '{nameof(ShipOrderSaga)}' will use 'ShipOrderSagaData' container.");
58-
return new ContainerInformation("ShipOrderSagaData", new PartitionKeyPath("/id"));
59-
});
60-
#endregion
61-
#region ContainerInformationFromHeaders
62-
transactionInformation.ExtractContainerInformationFromHeaders(headers =>
63-
{
64-
if (headers.TryGetValue(Headers.SagaType, out var sagaTypeHeader) && sagaTypeHeader.Contains(nameof(ShipOrderSaga)))
65-
{
66-
logger.LogInformation($"Message '{headers[Headers.EnclosedMessageTypes]}' destined to be handled by '{nameof(ShipOrderSaga)}' will use 'ShipOrderSagaData' container.");
64+
return new ContainerInformation("ShipOrderSagaData", new PartitionKeyPath("/id"));
65+
}
66+
return null;
67+
});
68+
#endregion
6769

68-
return new ContainerInformation("ShipOrderSagaData", new PartitionKeyPath("/id"));
69-
}
70-
return null;
71-
});
72-
#endregion
70+
cosmosClient.CreateDatabaseIfNotExistsAsync("Samples.CosmosDB.Container").Wait();
71+
var database = cosmosClient.GetDatabase("Samples.CosmosDB.Container");
72+
database.CreateContainerIfNotExistsAsync("ShipOrderSagaData", "/id").Wait();
7373

74-
cosmosClient.CreateDatabaseIfNotExistsAsync("Samples.CosmosDB.Container");
75-
var database = cosmosClient.GetDatabase("Samples.CosmosDB.Container");
76-
database.CreateContainerIfNotExistsAsync("ShipOrderSagaData", "/id");
74+
builder.UseNServiceBus(endpointConfiguration);
7775

78-
Console.ReadKey();
76+
var host = builder.Build();
77+
await host.StartAsync();
7978

80-
return endpointConfiguration;
81-
});
79+
logger.LogInformation("Server started successfully. Press any key to exit");
80+
81+
// Wait for input or termination
82+
if (Console.IsInputRedirected)
83+
{
84+
// In container or background mode, wait for termination signal
85+
await host.WaitForShutdownAsync();
86+
}
87+
else
88+
{
89+
// Interactive mode
90+
Console.ReadKey();
91+
await host.StopAsync();
92+
}
93+
}
8294
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
services:
2+
sample:
3+
image: cosmosdb-container-sample
4+
environment:
5+
- COSMOS_CONNECTION_STRING=${COSMOS_CONNECTION_STRING}
6+
networks:
7+
- cosmosdb-network
8+
9+
networks:
10+
cosmosdb-network:
11+
driver: bridge

0 commit comments

Comments
 (0)