diff --git a/.gitignore b/.gitignore
index 7724559c..43d837a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -431,3 +431,5 @@ FodyWeavers.xsd
**/*/azure.yaml
**/*/next-steps.md
+/samples/OrchardCore/OrchardCore.Cms/App_Data
+/samples/OrchardCore/OrchardCore.Cms/Localization
diff --git a/README.md b/README.md
index 80e3dcbb..e1040cf6 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ Sample highlights include:
- [Persisting data in composed containers using volume mounts](./samples/VolumeMount)
- [Working with database containers](./samples/DatabaseContainers)
- [Integrating clients apps like WinForms](./samples/ClientAppsIntegration)
+- [Integrating OrchardCore CMS](./samples/OrchardCore)
## eShop
diff --git a/samples/OrchardCore/.gitignore b/samples/OrchardCore/.gitignore
new file mode 100644
index 00000000..c20455c7
--- /dev/null
+++ b/samples/OrchardCore/.gitignore
@@ -0,0 +1,12 @@
+# =========================
+# Orchard specifics
+# =========================
+
+App_Data*/
+.vs/
+
+#exclude node modules
+node_modules/
+
+wwwroot
+**/Localization/**/*.po
diff --git a/samples/OrchardCore/Aspire/Aspire.AppHost/Aspire.AppHost.csproj b/samples/OrchardCore/Aspire/Aspire.AppHost/Aspire.AppHost.csproj
new file mode 100644
index 00000000..5bf1b8c9
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.AppHost/Aspire.AppHost.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OrchardCore/Aspire/Aspire.AppHost/Program.cs b/samples/OrchardCore/Aspire/Aspire.AppHost/Program.cs
new file mode 100644
index 00000000..7049023e
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.AppHost/Program.cs
@@ -0,0 +1,18 @@
+var builder = DistributedApplication.CreateBuilder(args);
+const int postgresPort = 62262;
+const string postgresUsername = "occms";
+const string postgresPassword = "OrchardCorePass";
+
+var cmsdb = builder.AddPostgresContainer("Postgres", postgresPort, postgresPassword);
+
+var redis = builder.AddRedisContainer("Redis", 50963);
+
+builder.AddProject("OrchardCore CMS")
+ .WithEnvironment("OrchardCore__Default__State", "Uninitialized")
+ .WithEnvironment("OrchardCore__Default__TablePrefix", "Default")
+ .WithEnvironment("OrchardCore__DatabaseProvider", "Postgres")
+ .WithEnvironment("OrchardCore__ConnectionString", $"host=localhost;port={postgresPort};database={postgresUsername};username={postgresUsername};password={postgresPassword}")
+ .WithReference(redis)
+ .WithReference(cmsdb);
+
+await builder.Build().RunAsync();
diff --git a/samples/OrchardCore/Aspire/Aspire.AppHost/Properties/launchSettings.json b/samples/OrchardCore/Aspire/Aspire.AppHost/Properties/launchSettings.json
new file mode 100644
index 00000000..42a35b84
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.AppHost/Properties/launchSettings.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:15264",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16296"
+ }
+ }
+ }
+}
diff --git a/samples/OrchardCore/Aspire/Aspire.AppHost/appsettings.Development.json b/samples/OrchardCore/Aspire/Aspire.AppHost/appsettings.Development.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.AppHost/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/samples/OrchardCore/Aspire/Aspire.AppHost/appsettings.json b/samples/OrchardCore/Aspire/Aspire.AppHost/appsettings.json
new file mode 100644
index 00000000..31c092aa
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.AppHost/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "Aspire.Hosting.Dcp": "Warning"
+ }
+ }
+}
diff --git a/samples/OrchardCore/Aspire/Aspire.ServiceDefaults/Aspire.ServiceDefaults.csproj b/samples/OrchardCore/Aspire/Aspire.ServiceDefaults/Aspire.ServiceDefaults.csproj
new file mode 100644
index 00000000..30d5c1fb
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.ServiceDefaults/Aspire.ServiceDefaults.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Library
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OrchardCore/Aspire/Aspire.ServiceDefaults/Extensions.cs b/samples/OrchardCore/Aspire/Aspire.ServiceDefaults/Extensions.cs
new file mode 100644
index 00000000..c59308d5
--- /dev/null
+++ b/samples/OrchardCore/Aspire/Aspire.ServiceDefaults/Extensions.cs
@@ -0,0 +1,119 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Logging;
+using OpenTelemetry.Logs;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
+
+namespace Microsoft.Extensions.Hosting;
+
+public static class Extensions
+{
+ public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
+ {
+ builder.ConfigureOpenTelemetry();
+
+ builder.AddDefaultHealthChecks();
+
+ builder.Services.AddServiceDiscovery();
+
+ builder.Services.ConfigureHttpClientDefaults(http =>
+ {
+ // Turn on resilience by default
+ http.AddStandardResilienceHandler();
+
+ // Turn on service discovery by default
+ http.UseServiceDiscovery();
+ });
+
+ return builder;
+ }
+
+ public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
+ {
+ builder.Logging.AddOpenTelemetry(logging =>
+ {
+ logging.IncludeFormattedMessage = true;
+ logging.IncludeScopes = true;
+ });
+
+ builder.Services.AddOpenTelemetry()
+ .WithMetrics(metrics =>
+ {
+ metrics.AddRuntimeInstrumentation()
+ .AddBuiltInMeters();
+ })
+ .WithTracing(tracing =>
+ {
+ if (builder.Environment.IsDevelopment())
+ {
+ // We want to view all traces in development
+ tracing.SetSampler(new AlwaysOnSampler());
+ }
+
+ tracing.AddAspNetCoreInstrumentation()
+ .AddGrpcClientInstrumentation()
+ .AddHttpClientInstrumentation();
+ });
+
+ builder.AddOpenTelemetryExporters();
+
+ return builder;
+ }
+
+ private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
+ {
+ var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
+
+ if (useOtlpExporter)
+ {
+ builder.Services.Configure(logging => logging.AddOtlpExporter());
+ builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
+ builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
+ }
+
+ // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
+ // builder.Services.AddOpenTelemetry()
+ // .WithMetrics(metrics => metrics.AddPrometheusExporter());
+
+ // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package)
+ // builder.Services.AddOpenTelemetry()
+ // .UseAzureMonitor();
+
+ return builder;
+ }
+
+ public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
+ {
+ builder.Services.AddHealthChecks()
+ // Add a default liveness check to ensure app is responsive
+ .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
+
+ return builder;
+ }
+
+ public static WebApplication MapDefaultEndpoints(this WebApplication app)
+ {
+ // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
+ // app.MapPrometheusScrapingEndpoint();
+
+ // All health checks must pass for app to be considered ready to accept traffic after starting
+ app.MapHealthChecks("/health");
+
+ // Only health checks tagged with the "live" tag must pass for app to be considered alive
+ app.MapHealthChecks("/alive", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("live")
+ });
+
+ return app;
+ }
+
+ private static MeterProviderBuilder AddBuiltInMeters(this MeterProviderBuilder meterProviderBuilder) =>
+ meterProviderBuilder.AddMeter(
+ "Microsoft.AspNetCore.Hosting",
+ "Microsoft.AspNetCore.Server.Kestrel",
+ "System.Net.Http");
+}
diff --git a/samples/OrchardCore/OrchardCore.Cms/NLog.config b/samples/OrchardCore/OrchardCore.Cms/NLog.config
new file mode 100644
index 00000000..772f22d4
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.Cms/NLog.config
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OrchardCore/OrchardCore.Cms/OrchardCore.Cms.csproj b/samples/OrchardCore/OrchardCore.Cms/OrchardCore.Cms.csproj
new file mode 100644
index 00000000..bd5a1f60
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.Cms/OrchardCore.Cms.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OrchardCore/OrchardCore.Cms/Program.cs b/samples/OrchardCore/OrchardCore.Cms/Program.cs
new file mode 100644
index 00000000..71482a2a
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.Cms/Program.cs
@@ -0,0 +1,21 @@
+using OrchardCore.Logging;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Host.UseNLogHost();
+builder.AddServiceDefaults();
+
+builder.Services.AddOrchardCms();
+
+var app = builder.Build();
+
+if (!app.Environment.IsDevelopment())
+{
+ app.UseExceptionHandler("/Error");
+ app.UseHsts();
+}
+
+app.UseStaticFiles();
+app.UseOrchardCore();
+
+await app.RunAsync();
diff --git a/samples/OrchardCore/OrchardCore.Cms/Properties/launchSettings.json b/samples/OrchardCore/OrchardCore.Cms/Properties/launchSettings.json
new file mode 100644
index 00000000..2efeda50
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.Cms/Properties/launchSettings.json
@@ -0,0 +1,38 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54444",
+ "sslPort": 44359
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5090",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7065;http://localhost:5090",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/samples/OrchardCore/OrchardCore.Cms/appsettings.Development.json b/samples/OrchardCore/OrchardCore.Cms/appsettings.Development.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.Cms/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/samples/OrchardCore/OrchardCore.Cms/appsettings.json b/samples/OrchardCore/OrchardCore.Cms/appsettings.json
new file mode 100644
index 00000000..5a26a881
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.Cms/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "OrchardCore": {
+
+ }
+}
diff --git a/samples/OrchardCore/OrchardCore.sln b/samples/OrchardCore/OrchardCore.sln
new file mode 100644
index 00000000..ab79c0bd
--- /dev/null
+++ b/samples/OrchardCore/OrchardCore.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34407.89
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.AppHost", "Aspire\Aspire.AppHost\Aspire.AppHost.csproj", "{402C833F-9FCE-4557-84D4-3AAAF74C8DE8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.ServiceDefaults", "Aspire\Aspire.ServiceDefaults\Aspire.ServiceDefaults.csproj", "{DE2BA7D5-D4FE-446E-AED3-771C7BDF5CAD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Cms", "OrchardCore.Cms\OrchardCore.Cms.csproj", "{58DC94EA-7B87-4FA9-B3D1-E20AD89E5692}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aspire", "Aspire", "{BC067F5D-BBC3-4159-9A65-F2B105BC0758}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {402C833F-9FCE-4557-84D4-3AAAF74C8DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {402C833F-9FCE-4557-84D4-3AAAF74C8DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {402C833F-9FCE-4557-84D4-3AAAF74C8DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {402C833F-9FCE-4557-84D4-3AAAF74C8DE8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DE2BA7D5-D4FE-446E-AED3-771C7BDF5CAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DE2BA7D5-D4FE-446E-AED3-771C7BDF5CAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DE2BA7D5-D4FE-446E-AED3-771C7BDF5CAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DE2BA7D5-D4FE-446E-AED3-771C7BDF5CAD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {58DC94EA-7B87-4FA9-B3D1-E20AD89E5692}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {58DC94EA-7B87-4FA9-B3D1-E20AD89E5692}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {58DC94EA-7B87-4FA9-B3D1-E20AD89E5692}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {58DC94EA-7B87-4FA9-B3D1-E20AD89E5692}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {402C833F-9FCE-4557-84D4-3AAAF74C8DE8} = {BC067F5D-BBC3-4159-9A65-F2B105BC0758}
+ {DE2BA7D5-D4FE-446E-AED3-771C7BDF5CAD} = {BC067F5D-BBC3-4159-9A65-F2B105BC0758}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {77E10717-33F7-4C6D-94C3-9426C68D966C}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/OrchardCore/README.md b/samples/OrchardCore/README.md
new file mode 100644
index 00000000..c3dd7eda
--- /dev/null
+++ b/samples/OrchardCore/README.md
@@ -0,0 +1,51 @@
+---
+languages:
+- csharp
+products:
+- dotnet
+- dotnet-aspire
+page_type: sample
+name: ".NET Aspire OrchardCore sample app"
+urlFragment: "aspire-orchard-core"
+description: "A sample .NET Aspire app that shows how to use OrchardCore"
+---
+
+# .NET Aspire OrchardCore CMS sample app
+
+This is a simple .NET app that shows how to use OrchardCore with .NET Aspire orchestration.
+
+## Demonstrates
+
+- How to configure a .NET Aspire app to work with OrchardCore
+
+## Sample prerequisites
+
+- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
+- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
+- **Optional** [Visual Studio 2022 17.9 Preview](https://visualstudio.microsoft.com/vs/preview/)
+
+
+## Running the sample
+
+To download and run the sample, follow these steps:
+
+### Run the project using Visual Studio
+
+To run the sample project using Visual Studio, open Visual Studio (2022 or later), then:
+
+ 1. On the menu bar, choose **File** > **Open** > **Project/Solution**.
+ 2. Navigate to the folder that holds the unzipped sample code, and open the solution (.sln) file.
+ 3. Right click the _Aspire.AppHost_ project in the solution explore and choose it as the startup project.
+ 4. Choose the F5 key to run with debugging, or Ctrl+F5 keys to run the project without debugging.
+
+### Run the project using command line
+
+To run the .NET Aspire app, open a command line console and change directory to the OrchardCore solution folder. Then execute the following command:
+
+``` bash
+dotnet run --project Aspire/Aspire.AppHost
+```
+
+On the **Projects** page, click on one of the endpoints (OrchardCore CMS) for the listed project. This launches the simple .NET app.
+
+For more information about using OrchardCore, see the [OrchardCore documentation](https://docs.orchardcore.net/en/latest/).