diff --git a/docs/database/includes/sql-app-host.md b/docs/database/includes/sql-app-host.md index 1bc3a48d27..f043f91283 100644 --- a/docs/database/includes/sql-app-host.md +++ b/docs/database/includes/sql-app-host.md @@ -25,20 +25,7 @@ For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-pac In your app host project, call to add and return a SQL Server resource builder. Chain a call to the returned resource builder to , to add SQL Server database resource. -```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var sql = builder.AddSqlServer("sql") - .WithLifetime(ContainerLifetime.Persistent); - -var db = sql.AddDatabase("database"); - -builder.AddProject() - .WithReference(db) - .WaitFor(db); - -// After adding all resources, run the app... -``` +:::code language="csharp" source="../snippets/sql-server-integration/AspireApp.AppHost/AppHost.cs"::: > [!NOTE] > The SQL Server container is slow to start, so it's best to use a _persistent_ lifetime to avoid unnecessary restarts. For more information, see [Container resource lifetime](../../fundamentals/orchestrate-resources.md#container-resource-lifetime). @@ -81,53 +68,9 @@ IF CREATE DATABASE []; ``` - - -To alter the default script, chain a call to the `WithCreationScript` method on the database resource builder: - -```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var sql = builder.AddSqlServer("sql") - .WithLifetime(ContainerLifetime.Persistent); - -var databaseName = "app_db"; -var creationScript = $$""" - IF DB_ID('{{databaseName}}') IS NULL - CREATE DATABASE [{{databaseName}}]; - GO - - -- Use the database - USE [{{databaseName}}]; - GO - - -- Create the todos table - CREATE TABLE todos ( - id INT PRIMARY KEY AUTO_INCREMENT, -- Unique ID for each todo - title VARCHAR(255) NOT NULL, -- Short description of the task - description TEXT, -- Optional detailed description - is_completed BOOLEAN DEFAULT FALSE, -- Completion status - due_date DATE, -- Optional due date - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP - ); - GO - - """; - -var db = sql.AddDatabase(databaseName) - .WithCreationScript(creationScript); - -builder.AddProject() - .WithReference(db) - .WaitFor(db); - -// After adding all resources, run the app... -``` +:::code language="csharp" source="../snippets/sql-server-creation-script/AspireApp.AppHost/AppHost.cs"::: The preceding example creates a database named `app_db` with a single `todos` table. The SQL script is executed when the database resource is created. The script is passed as a string to the `WithCreationScript` method, which is then executed in the context of the SQL Server resource. @@ -135,20 +78,7 @@ The preceding example creates a database named `app_db` with a single `todos` ta To add a data volume to the SQL Server resource, call the method on the SQL Server resource: -```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var sql = builder.AddSqlServer("sql") - .WithDataVolume(); - -var db = sql.AddDatabase("database"); - -builder.AddProject() - .WithReference(db) - .WaitFor(db); - -// After adding all resources, run the app... -``` +:::code language="csharp" source="../snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs"::: The data volume is used to persist the SQL Server data outside the lifecycle of its container. The data volume is mounted at the `/var/opt/mssql` path in the SQL Server container and when a `name` parameter isn't provided, the name is generated at random. For more information on data volumes and details on why they're preferred over [bind mounts](#add-sql-server-resource-with-data-bind-mount), see [Docker docs: Volumes](https://docs.docker.com/engine/storage/volumes). @@ -159,20 +89,7 @@ The data volume is used to persist the SQL Server data outside the lifecycle of To add a data bind mount to the SQL Server resource, call the method: -```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var sql = builder.AddSqlServer("sql") - .WithDataBindMount(source: @"C:\SqlServer\Data"); - -var db = sql.AddDatabase("database"); - -builder.AddProject() - .WithReference(db) - .WaitFor(db); - -// After adding all resources, run the app... -``` +:::code language="csharp" source="../snippets/sql-server-bind-mount/AspireApp.AppHost/AppHost.cs"::: [!INCLUDE [data-bind-mount-vs-volumes](../../includes/data-bind-mount-vs-volumes.md)] @@ -182,20 +99,7 @@ Data bind mounts rely on the host machine's filesystem to persist the SQL Server When you want to explicitly provide the password used by the container image, you can provide these credentials as parameters. Consider the following alternative example: -```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var password = builder.AddParameter("password", secret: true); - -var sql = builder.AddSqlServer("sql", password); -var db = sql.AddDatabase("database"); - -builder.AddProject() - .WithReference(db) - .WaitFor(db); - -// After adding all resources, run the app... -``` +:::code language="csharp" source="../snippets/sql-server-parameters/AspireApp.AppHost/AppHost.cs"::: For more information on providing parameters, see [External parameters](../../fundamentals/external-parameters.md). diff --git a/docs/database/snippets/sql-server-bind-mount/.aspire/settings.json b/docs/database/snippets/sql-server-bind-mount/.aspire/settings.json new file mode 100644 index 0000000000..5199601f05 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../AspireApp.AppHost/AspireApp.AppHost.csproj" +} \ No newline at end of file diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/AppHost.cs b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/AppHost.cs new file mode 100644 index 0000000000..36755d9126 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/AppHost.cs @@ -0,0 +1,14 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithDataBindMount(source: @"C:\SqlServer\Data"); + +var db = sql.AddDatabase("database"); + +builder.AddProject("exampleproject") + .WithReference(db) + .WaitFor(db); + +// After adding all resources, run the app... + +builder.Build().Run(); diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/AspireApp.AppHost.csproj new file mode 100644 index 0000000000..999c350b09 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -0,0 +1,19 @@ + + + + + + Exe + net9.0 + enable + enable + ba7d27f9-faf2-4dc2-9910-fa27dbbfefe4 + + + + + + + + + diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/Properties/launchSettings.json b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..3e3b3f6812 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17260;http://localhost:15287", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21100", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22102" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15287", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19061", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20241" + } + } + } +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/appsettings.Development.json b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/appsettings.json b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj new file mode 100644 index 0000000000..6568b3dcfb --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/AspireApp.ExampleProject.http b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/AspireApp.ExampleProject.http new file mode 100644 index 0000000000..38b8ac33c1 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/AspireApp.ExampleProject.http @@ -0,0 +1,6 @@ +@AspireApp.ExampleProject_HostAddress = http://localhost:5108 + +GET {{AspireApp.ExampleProject_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/Program.cs b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/Program.cs new file mode 100644 index 0000000000..bc2df0077b --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/Program.cs @@ -0,0 +1,32 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/Properties/launchSettings.json b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/Properties/launchSettings.json new file mode 100644 index 0000000000..7640d96f94 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5108", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/appsettings.Development.json b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/appsettings.json b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ExampleProject/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/database/snippets/sql-server-bind-mount/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj new file mode 100644 index 0000000000..71f1baa7bc --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.ServiceDefaults/Extensions.cs b/docs/database/snippets/sql-server-bind-mount/AspireApp.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..112c128147 --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/docs/database/snippets/sql-server-bind-mount/AspireApp.sln b/docs/database/snippets/sql-server-bind-mount/AspireApp.sln new file mode 100644 index 0000000000..35cfb97b5a --- /dev/null +++ b/docs/database/snippets/sql-server-bind-mount/AspireApp.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.AppHost", "AspireApp.AppHost\AspireApp.AppHost.csproj", "{CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.ServiceDefaults", "AspireApp.ServiceDefaults\AspireApp.ServiceDefaults.csproj", "{D42759BB-0845-4327-AA2C-15A428919F6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.ExampleProject", "AspireApp.ExampleProject\AspireApp.ExampleProject.csproj", "{C84F9F6E-CA6D-4337-8138-0BA5316D8A86}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Debug|x64.ActiveCfg = Debug|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Debug|x64.Build.0 = Debug|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Debug|x86.ActiveCfg = Debug|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Debug|x86.Build.0 = Debug|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Release|Any CPU.Build.0 = Release|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Release|x64.ActiveCfg = Release|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Release|x64.Build.0 = Release|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Release|x86.ActiveCfg = Release|Any CPU + {CF2B4AE6-D480-4DCD-BFFD-6B0F4898D39B}.Release|x86.Build.0 = Release|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Debug|x64.ActiveCfg = Debug|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Debug|x64.Build.0 = Debug|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Debug|x86.ActiveCfg = Debug|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Debug|x86.Build.0 = Debug|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Release|Any CPU.Build.0 = Release|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Release|x64.ActiveCfg = Release|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Release|x64.Build.0 = Release|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Release|x86.ActiveCfg = Release|Any CPU + {D42759BB-0845-4327-AA2C-15A428919F6B}.Release|x86.Build.0 = Release|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Debug|x64.ActiveCfg = Debug|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Debug|x64.Build.0 = Debug|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Debug|x86.ActiveCfg = Debug|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Debug|x86.Build.0 = Debug|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Release|Any CPU.Build.0 = Release|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Release|x64.ActiveCfg = Release|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Release|x64.Build.0 = Release|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Release|x86.ActiveCfg = Release|Any CPU + {C84F9F6E-CA6D-4337-8138-0BA5316D8A86}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E27D2557-AC35-4EC4-B017-00BBEB0F94D1} + EndGlobalSection +EndGlobal diff --git a/docs/database/snippets/sql-server-creation-script/.aspire/settings.json b/docs/database/snippets/sql-server-creation-script/.aspire/settings.json new file mode 100644 index 0000000000..5199601f05 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../AspireApp.AppHost/AspireApp.AppHost.csproj" +} \ No newline at end of file diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/AppHost.cs b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/AppHost.cs new file mode 100644 index 0000000000..eefdd683af --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/AppHost.cs @@ -0,0 +1,38 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithLifetime(ContainerLifetime.Persistent); + +var databaseName = "app-db"; +var creationScript = $$""" + IF DB_ID('{{databaseName}}') IS NULL + CREATE DATABASE [{{databaseName}}]; + GO + + -- Use the database + USE [{{databaseName}}]; + GO + + -- Create the todos table + CREATE TABLE todos ( + id INT PRIMARY KEY IDENTITY(1,1), -- Unique ID for each todo + title VARCHAR(255) NOT NULL, -- Short description of the task + description TEXT, -- Optional detailed description + is_completed BIT DEFAULT 0, -- Completion status + due_date DATE, -- Optional due date + created_at DATETIME DEFAULT GETDATE() -- Creation timestamp + ); + GO + + """; + +var db = sql.AddDatabase(databaseName) + .WithCreationScript(creationScript); + +builder.AddProject("exampleproject") + .WithReference(db) + .WaitFor(db); + +// After adding all resources, run the app... + +builder.Build().Run(); diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/AspireApp.AppHost.csproj new file mode 100644 index 0000000000..05939cec75 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -0,0 +1,19 @@ + + + + + + Exe + net9.0 + enable + enable + 575439fe-56ba-4263-9337-1c6f8afcf9f7 + + + + + + + + + diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/Properties/launchSettings.json b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..862ef06191 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17232;http://localhost:15013", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21005", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22075" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15013", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19280", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20245" + } + } + } +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/appsettings.Development.json b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/appsettings.json b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj new file mode 100644 index 0000000000..6568b3dcfb --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/AspireApp.ExampleProject.http b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/AspireApp.ExampleProject.http new file mode 100644 index 0000000000..3a93886816 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/AspireApp.ExampleProject.http @@ -0,0 +1,6 @@ +@AspireApp.ExampleProject_HostAddress = http://localhost:5290 + +GET {{AspireApp.ExampleProject_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/Program.cs b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/Program.cs new file mode 100644 index 0000000000..bc2df0077b --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/Program.cs @@ -0,0 +1,32 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/Properties/launchSettings.json b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/Properties/launchSettings.json new file mode 100644 index 0000000000..7241022e2c --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5290", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/appsettings.Development.json b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/appsettings.json b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ExampleProject/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/database/snippets/sql-server-creation-script/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj new file mode 100644 index 0000000000..71f1baa7bc --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.ServiceDefaults/Extensions.cs b/docs/database/snippets/sql-server-creation-script/AspireApp.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..112c128147 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/docs/database/snippets/sql-server-creation-script/AspireApp.sln b/docs/database/snippets/sql-server-creation-script/AspireApp.sln new file mode 100644 index 0000000000..5509239900 --- /dev/null +++ b/docs/database/snippets/sql-server-creation-script/AspireApp.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.AppHost", "AspireApp.AppHost\AspireApp.AppHost.csproj", "{418960D7-7A8A-49FF-A45D-17C136B27E88}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.ServiceDefaults", "AspireApp.ServiceDefaults\AspireApp.ServiceDefaults.csproj", "{AF2400EB-F0FD-44D5-BF0A-E4F717346144}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.ExampleProject", "AspireApp.ExampleProject\AspireApp.ExampleProject.csproj", "{0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Debug|x64.ActiveCfg = Debug|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Debug|x64.Build.0 = Debug|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Debug|x86.ActiveCfg = Debug|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Debug|x86.Build.0 = Debug|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Release|Any CPU.Build.0 = Release|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Release|x64.ActiveCfg = Release|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Release|x64.Build.0 = Release|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Release|x86.ActiveCfg = Release|Any CPU + {418960D7-7A8A-49FF-A45D-17C136B27E88}.Release|x86.Build.0 = Release|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Debug|x64.ActiveCfg = Debug|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Debug|x64.Build.0 = Debug|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Debug|x86.ActiveCfg = Debug|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Debug|x86.Build.0 = Debug|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Release|Any CPU.Build.0 = Release|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Release|x64.ActiveCfg = Release|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Release|x64.Build.0 = Release|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Release|x86.ActiveCfg = Release|Any CPU + {AF2400EB-F0FD-44D5-BF0A-E4F717346144}.Release|x86.Build.0 = Release|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Debug|x64.ActiveCfg = Debug|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Debug|x64.Build.0 = Debug|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Debug|x86.ActiveCfg = Debug|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Debug|x86.Build.0 = Debug|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Release|Any CPU.Build.0 = Release|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Release|x64.ActiveCfg = Release|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Release|x64.Build.0 = Release|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Release|x86.ActiveCfg = Release|Any CPU + {0CE61ABD-54A4-4B23-BF85-3C9D481CE4F9}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4C838129-2B7D-4189-B9D6-248DE78F5CD8} + EndGlobalSection +EndGlobal diff --git a/docs/database/snippets/sql-server-data-volume/.aspire/settings.json b/docs/database/snippets/sql-server-data-volume/.aspire/settings.json new file mode 100644 index 0000000000..5199601f05 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../AspireApp.AppHost/AspireApp.AppHost.csproj" +} \ No newline at end of file diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs new file mode 100644 index 0000000000..65d81bdc0f --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs @@ -0,0 +1,14 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithDataVolume(); + +var db = sql.AddDatabase("database"); + +builder.AddProject("exampleproject") + .WithReference(db) + .WaitFor(db); + +// After adding all resources, run the app... + +builder.Build().Run(); diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs~ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs~ new file mode 100644 index 0000000000..4442917406 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AppHost.cs~ @@ -0,0 +1,13 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithDataVolume(); + +var db = sql.AddDatabase("database"); + +builder.AddProject("exampleproject") + .WithReference(db) + .WaitFor(db); + +// After adding all resources, run the app... +builder.Build().Run(); diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AspireApp.AppHost.csproj new file mode 100644 index 0000000000..4d727c44f2 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -0,0 +1,19 @@ + + + + + + Exe + net9.0 + enable + enable + 7fdcb062-4c81-492d-8e06-e96f7b8035ee + + + + + + + + + diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/Properties/launchSettings.json b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..ba35446cb4 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17223;http://localhost:15043", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21074", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22138" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15043", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19285", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20066" + } + } + } +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/appsettings.Development.json b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/appsettings.json b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj new file mode 100644 index 0000000000..6568b3dcfb --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/AspireApp.ExampleProject.http b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/AspireApp.ExampleProject.http new file mode 100644 index 0000000000..82638813a2 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/AspireApp.ExampleProject.http @@ -0,0 +1,6 @@ +@AspireApp.ExampleProject_HostAddress = http://localhost:5022 + +GET {{AspireApp.ExampleProject_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/Program.cs b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/Program.cs new file mode 100644 index 0000000000..bc2df0077b --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/Program.cs @@ -0,0 +1,32 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/Properties/launchSettings.json b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/Properties/launchSettings.json new file mode 100644 index 0000000000..1fb7ae9b97 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5022", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/appsettings.Development.json b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/appsettings.json b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ExampleProject/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/database/snippets/sql-server-data-volume/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj new file mode 100644 index 0000000000..71f1baa7bc --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.ServiceDefaults/Extensions.cs b/docs/database/snippets/sql-server-data-volume/AspireApp.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..112c128147 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/docs/database/snippets/sql-server-data-volume/AspireApp.sln b/docs/database/snippets/sql-server-data-volume/AspireApp.sln new file mode 100644 index 0000000000..664bb7af96 --- /dev/null +++ b/docs/database/snippets/sql-server-data-volume/AspireApp.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.AppHost", "AspireApp.AppHost\AspireApp.AppHost.csproj", "{9C09F319-4620-462E-8D2E-A7151B6E22B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.ServiceDefaults", "AspireApp.ServiceDefaults\AspireApp.ServiceDefaults.csproj", "{874239A1-3B5C-470D-9C95-3CFA52F625BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.ExampleProject", "AspireApp.ExampleProject\AspireApp.ExampleProject.csproj", "{231E3428-CD83-49E6-8278-1B2C6988609A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Debug|x64.Build.0 = Debug|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Debug|x86.ActiveCfg = Debug|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Debug|x86.Build.0 = Debug|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Release|Any CPU.Build.0 = Release|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Release|x64.ActiveCfg = Release|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Release|x64.Build.0 = Release|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Release|x86.ActiveCfg = Release|Any CPU + {9C09F319-4620-462E-8D2E-A7151B6E22B6}.Release|x86.Build.0 = Release|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Debug|x64.Build.0 = Debug|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Debug|x86.Build.0 = Debug|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Release|Any CPU.Build.0 = Release|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Release|x64.ActiveCfg = Release|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Release|x64.Build.0 = Release|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Release|x86.ActiveCfg = Release|Any CPU + {874239A1-3B5C-470D-9C95-3CFA52F625BD}.Release|x86.Build.0 = Release|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Debug|x64.ActiveCfg = Debug|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Debug|x64.Build.0 = Debug|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Debug|x86.ActiveCfg = Debug|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Debug|x86.Build.0 = Debug|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Release|Any CPU.Build.0 = Release|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Release|x64.ActiveCfg = Release|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Release|x64.Build.0 = Release|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Release|x86.ActiveCfg = Release|Any CPU + {231E3428-CD83-49E6-8278-1B2C6988609A}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8B552053-4EC3-41E7-B906-A4FD85323EFE} + EndGlobalSection +EndGlobal diff --git a/docs/database/snippets/sql-server-integration/.aspire/settings.json b/docs/database/snippets/sql-server-integration/.aspire/settings.json new file mode 100644 index 0000000000..5199601f05 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../AspireApp.AppHost/AspireApp.AppHost.csproj" +} \ No newline at end of file diff --git a/docs/database/snippets/sql-server-integration/AspireApp.AppHost/AppHost.cs b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/AppHost.cs new file mode 100644 index 0000000000..9ef5ea15b7 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/AppHost.cs @@ -0,0 +1,14 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithLifetime(ContainerLifetime.Persistent); + +var db = sql.AddDatabase("database"); + +builder.AddProject("exampleproject") + .WithReference(db) + .WaitFor(db); + +// After adding all resources, run the app... + +builder.Build().Run(); diff --git a/docs/database/snippets/sql-server-integration/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/AspireApp.AppHost.csproj new file mode 100644 index 0000000000..73c40ee85d --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -0,0 +1,19 @@ + + + + + + Exe + net9.0 + enable + enable + 09bb73b7-ba70-46e3-b39f-0e3c876566ac + + + + + + + + + diff --git a/docs/database/snippets/sql-server-integration/AspireApp.AppHost/Properties/launchSettings.json b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..80ad20ad4e --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17251;http://localhost:15058", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21238", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22037" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15058", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19034", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20085" + } + } + } +} diff --git a/docs/database/snippets/sql-server-integration/AspireApp.AppHost/appsettings.Development.json b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-integration/AspireApp.AppHost/appsettings.json b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-integration/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/database/snippets/sql-server-integration/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj new file mode 100644 index 0000000000..71f1baa7bc --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/database/snippets/sql-server-integration/AspireApp.ServiceDefaults/Extensions.cs b/docs/database/snippets/sql-server-integration/AspireApp.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..112c128147 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/docs/database/snippets/sql-server-integration/AspireApp.sln b/docs/database/snippets/sql-server-integration/AspireApp.sln new file mode 100644 index 0000000000..515d110842 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/AspireApp.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.AppHost", "AspireApp.AppHost\AspireApp.AppHost.csproj", "{2DF28CB6-4394-4E84-A615-017EF99888DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.ServiceDefaults", "AspireApp.ServiceDefaults\AspireApp.ServiceDefaults.csproj", "{C86F7275-7F93-4AF7-85F2-E2E05194B075}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleProject", "ExampleProject\ExampleProject.csproj", "{B890F9F7-875D-43AC-8725-FFD0096B1765}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Debug|x64.ActiveCfg = Debug|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Debug|x64.Build.0 = Debug|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Debug|x86.ActiveCfg = Debug|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Debug|x86.Build.0 = Debug|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Release|Any CPU.Build.0 = Release|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Release|x64.ActiveCfg = Release|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Release|x64.Build.0 = Release|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Release|x86.ActiveCfg = Release|Any CPU + {2DF28CB6-4394-4E84-A615-017EF99888DA}.Release|x86.Build.0 = Release|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Debug|x64.ActiveCfg = Debug|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Debug|x64.Build.0 = Debug|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Debug|x86.ActiveCfg = Debug|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Debug|x86.Build.0 = Debug|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Release|Any CPU.Build.0 = Release|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Release|x64.ActiveCfg = Release|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Release|x64.Build.0 = Release|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Release|x86.ActiveCfg = Release|Any CPU + {C86F7275-7F93-4AF7-85F2-E2E05194B075}.Release|x86.Build.0 = Release|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Debug|x64.ActiveCfg = Debug|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Debug|x64.Build.0 = Debug|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Debug|x86.ActiveCfg = Debug|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Debug|x86.Build.0 = Debug|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Release|Any CPU.Build.0 = Release|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Release|x64.ActiveCfg = Release|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Release|x64.Build.0 = Release|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Release|x86.ActiveCfg = Release|Any CPU + {B890F9F7-875D-43AC-8725-FFD0096B1765}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4B4E8FFE-B1E5-4894-8684-9B5702273415} + EndGlobalSection +EndGlobal diff --git a/docs/database/snippets/sql-server-integration/ExampleProject/ExampleProject.csproj b/docs/database/snippets/sql-server-integration/ExampleProject/ExampleProject.csproj new file mode 100644 index 0000000000..e17a73d7f0 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/ExampleProject/ExampleProject.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + diff --git a/docs/database/snippets/sql-server-integration/ExampleProject/ExampleProject.http b/docs/database/snippets/sql-server-integration/ExampleProject/ExampleProject.http new file mode 100644 index 0000000000..bb05b69870 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/ExampleProject/ExampleProject.http @@ -0,0 +1,6 @@ +@ExampleProject_HostAddress = http://localhost:5105 + +GET {{ExampleProject_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/database/snippets/sql-server-integration/ExampleProject/Program.cs b/docs/database/snippets/sql-server-integration/ExampleProject/Program.cs new file mode 100644 index 0000000000..ee9d65d633 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/ExampleProject/Program.cs @@ -0,0 +1,41 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast"); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/docs/database/snippets/sql-server-integration/ExampleProject/Properties/launchSettings.json b/docs/database/snippets/sql-server-integration/ExampleProject/Properties/launchSettings.json new file mode 100644 index 0000000000..6554a97c5f --- /dev/null +++ b/docs/database/snippets/sql-server-integration/ExampleProject/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5105", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7195;http://localhost:5105", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/database/snippets/sql-server-integration/ExampleProject/appsettings.Development.json b/docs/database/snippets/sql-server-integration/ExampleProject/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-integration/ExampleProject/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-integration/ExampleProject/appsettings.json b/docs/database/snippets/sql-server-integration/ExampleProject/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/database/snippets/sql-server-integration/ExampleProject/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/database/snippets/sql-server-parameters/.aspire/settings.json b/docs/database/snippets/sql-server-parameters/.aspire/settings.json new file mode 100644 index 0000000000..5199601f05 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../AspireApp.AppHost/AspireApp.AppHost.csproj" +} \ No newline at end of file diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/AppHost.cs b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/AppHost.cs new file mode 100644 index 0000000000..75def2868c --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/AppHost.cs @@ -0,0 +1,14 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var password = builder.AddParameter("password", secret: true); + +var sql = builder.AddSqlServer("sql", password); +var db = sql.AddDatabase("database"); + +builder.AddProject("exampleproject") + .WithReference(db) + .WaitFor(db); + +// After adding all resources, run the app... + +builder.Build().Run(); diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/AspireApp.AppHost.csproj new file mode 100644 index 0000000000..a3e6314bf2 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -0,0 +1,19 @@ + + + + + + Exe + net9.0 + enable + enable + f463fce3-f02f-432a-b4ee-1bdcc3015cb0 + + + + + + + + + diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/Properties/launchSettings.json b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..bdfe7909da --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17281;http://localhost:15298", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21154", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22055" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15298", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19219", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20235" + } + } + } +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/appsettings.Development.json b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/appsettings.json b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/appsettings.json new file mode 100644 index 0000000000..a494e00878 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.AppHost/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + }, + "Parameters": { + "password": "Secret-p@ssw0rd" + } +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj new file mode 100644 index 0000000000..6568b3dcfb --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/AspireApp.ExampleProject.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/AspireApp.ExampleProject.http b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/AspireApp.ExampleProject.http new file mode 100644 index 0000000000..8e06d022e3 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/AspireApp.ExampleProject.http @@ -0,0 +1,6 @@ +@AspireApp.ExampleProject_HostAddress = http://localhost:5263 + +GET {{AspireApp.ExampleProject_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/Program.cs b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/Program.cs new file mode 100644 index 0000000000..bc2df0077b --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/Program.cs @@ -0,0 +1,32 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/Properties/launchSettings.json b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/Properties/launchSettings.json new file mode 100644 index 0000000000..c509d6b297 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5263", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/appsettings.Development.json b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/appsettings.json b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ExampleProject/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/database/snippets/sql-server-parameters/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj new file mode 100644 index 0000000000..71f1baa7bc --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.ServiceDefaults/Extensions.cs b/docs/database/snippets/sql-server-parameters/AspireApp.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..112c128147 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + 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) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/docs/database/snippets/sql-server-parameters/AspireApp.sln b/docs/database/snippets/sql-server-parameters/AspireApp.sln new file mode 100644 index 0000000000..96be3cc047 --- /dev/null +++ b/docs/database/snippets/sql-server-parameters/AspireApp.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.AppHost", "AspireApp.AppHost\AspireApp.AppHost.csproj", "{34B752E1-F73E-481C-8507-4BED4466ABE7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp.ServiceDefaults", "AspireApp.ServiceDefaults\AspireApp.ServiceDefaults.csproj", "{F5396120-AFFD-417B-AE27-88A68AE7BD56}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.ExampleProject", "AspireApp.ExampleProject\AspireApp.ExampleProject.csproj", "{9815C832-362C-44F9-AC17-E776B0AB033C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Debug|x64.ActiveCfg = Debug|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Debug|x64.Build.0 = Debug|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Debug|x86.Build.0 = Debug|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Release|Any CPU.Build.0 = Release|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Release|x64.ActiveCfg = Release|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Release|x64.Build.0 = Release|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Release|x86.ActiveCfg = Release|Any CPU + {34B752E1-F73E-481C-8507-4BED4466ABE7}.Release|x86.Build.0 = Release|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Debug|x64.ActiveCfg = Debug|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Debug|x64.Build.0 = Debug|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Debug|x86.ActiveCfg = Debug|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Debug|x86.Build.0 = Debug|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Release|Any CPU.Build.0 = Release|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Release|x64.ActiveCfg = Release|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Release|x64.Build.0 = Release|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Release|x86.ActiveCfg = Release|Any CPU + {F5396120-AFFD-417B-AE27-88A68AE7BD56}.Release|x86.Build.0 = Release|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Debug|x64.ActiveCfg = Debug|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Debug|x64.Build.0 = Debug|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Debug|x86.ActiveCfg = Debug|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Debug|x86.Build.0 = Debug|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Release|Any CPU.Build.0 = Release|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Release|x64.ActiveCfg = Release|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Release|x64.Build.0 = Release|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Release|x86.ActiveCfg = Release|Any CPU + {9815C832-362C-44F9-AC17-E776B0AB033C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C0D06A39-4CEB-4DC7-B138-B78BC6484375} + EndGlobalSection +EndGlobal