From e182cb0fb76cc13998ae22a73892c8a76ae9f33a Mon Sep 17 00:00:00 2001 From: David Ebbo Date: Tue, 23 Jul 2024 17:54:39 +0200 Subject: [PATCH 1/2] Fix supported -> support typo (#1395) --- docs/deployment/azure/aca-deployment-azd-in-depth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deployment/azure/aca-deployment-azd-in-depth.md b/docs/deployment/azure/aca-deployment-azd-in-depth.md index a5c3311fc3..18db2fc981 100644 --- a/docs/deployment/azure/aca-deployment-azd-in-depth.md +++ b/docs/deployment/azure/aca-deployment-azd-in-depth.md @@ -41,7 +41,7 @@ curl -fsSL https://aka.ms/install-azd.sh | bash ## How Azure Developer CLI integration works -The `azd init` workflow provides customized supported for .NET Aspire projects. The following diagram illustrates how this flow works conceptually and how `azd` and .NET Aspire are integrated: +The `azd init` workflow provides customized support for .NET Aspire projects. The following diagram illustrates how this flow works conceptually and how `azd` and .NET Aspire are integrated: :::image type="content" source="media/azd-internals.png" alt-text="Illustration of internal processing of `azd` when deploying .NET Aspire project."::: From 3a8318e6036a98c80217ab95d7cab1ec2419ea7f Mon Sep 17 00:00:00 2001 From: David Pine Date: Tue, 23 Jul 2024 14:11:49 -0500 Subject: [PATCH 2/2] Content for .NET Aspire - Release 8.1 (#1384) * Initial placeholder bits, to let other's help out * Fix MD lint * Add elasticsearch bits * Add Redis host includes * Fix hosting packages * Fix the host toggles for zones * Dockerfile docs. * Add note for event hubs emulator. * WIP: Python. * Update the zone name * Fix the 'missed one' :P * Sync Elasticsearch * Edit pass on withdockerfile content * Add Milvus * Add Milvus to TOC * A bit of clean for python * Apply suggestions from code review Co-authored-by: Alireza Baloochi * Added logos and overview entries * Fix image * Add snippets and address https://github.com/dotnet/docs-aspire/issues/1039#issuecomment-2225869375 * Lots of updates * Getting closer but still broken * Rename a bit * A few more updates * Finialize python doc with screenshot. * Apply suggestions from code review * Let's go, looking good * Add a section in the overview detailing RESP * Add Keycloak * A quick edit pass * Added resource types * Add Azure Web PubSub component * Touch dates * Bump all versions * Fix alt-text * A few API fixes --------- Co-authored-by: Mitch Denny Co-authored-by: Alireza Baloochi --- .gitignore | 3 + docfx.json | 2 + docs/app-host/withdockerfile.md | 115 ++++++++++ docs/authentication/keycloak-component.md | 134 +++++++++++ docs/caching/includes/garnet-app-host.md | 33 +++ docs/caching/includes/redis-app-host.md | 17 +- docs/caching/includes/valkey-app-host.md | 33 +++ .../stackexchange-redis-caching-overview.md | 31 ++- docs/caching/stackexchange-redis-component.md | 74 ++++-- ...nge-redis-distributed-caching-component.md | 22 +- ...exchange-redis-output-caching-component.md | 24 +- docs/database/milvus-component.md | 162 ++++++++++++++ .../AspireApp.ApiService.csproj | 2 +- .../AspireApp.AppHost.csproj | 4 +- .../AspireApp.ServiceDefaults.csproj | 2 +- .../AspireSql.AppHost.csproj | 4 +- .../AspireSql.ServiceDefaults.csproj | 2 +- .../AspireSql.AppHost.csproj | 4 +- .../AspireSql.ServiceDefaults.csproj | 2 +- .../AspireSQLEFCore.AppHost.csproj | 4 +- .../AspireSQLEFCore.ServiceDefaults.csproj | 2 +- .../AspireSQLEFCore/AspireSQLEFCore.csproj | 2 +- .../AppHost.Bicep/AppHost.Bicep.csproj | 4 +- .../MailDev.Hosting/MailDev.Hosting.csproj | 2 +- .../MailDevResource.AppHost.csproj | 2 +- .../MailDevResource.ServiceDefaults.csproj | 2 +- .../MailDev.Hosting/MailDev.Hosting.csproj | 2 +- .../MailDevResource.AppHost.csproj | 2 +- .../MailDevResource.ServiceDefaults.csproj | 2 +- .../MailKit.Client/MailKit.Client.csproj | 2 +- .../MailDev.Hosting/MailDev.Hosting.csproj | 2 +- .../MailDevResource.AppHost.csproj | 2 +- .../MailDevResource.ServiceDefaults.csproj | 2 +- .../MailKit.Client/MailKit.Client.csproj | 2 +- .../Dapr/Dapr.AppHost/Dapr.AppHost.csproj | 4 +- .../Dapr.ServiceDefaults.csproj | 2 +- .../OrleansAppHost/OrleansAppHost.csproj | 8 +- .../OrleansServiceDefaults.csproj | 2 +- docs/fundamentals/components-overview.md | 10 +- .../media/icons/AzureWebPubSub_256x.png | Bin 0 -> 10709 bytes .../media/icons/Elastic_logo_256x.png | Bin 0 -> 20759 bytes docs/fundamentals/media/icons/Milvus_256x.png | Bin 0 -> 17670 bytes .../AspireApp.AppHost.csproj | 4 +- .../AspireApp.ServiceDefaults.csproj | 2 +- .../Healthz.ServiceDefaults.csproj | 2 +- .../Networking.AppHost.csproj | 4 +- .../Networking.ServiceDefaults.csproj | 2 +- .../Parameters.ApiService.csproj | 2 +- .../Parameters.AppHost.csproj | 4 +- .../Parameters.ServiceDefaults.csproj | 2 +- .../YourAppName.ServiceDefaults.csproj | 2 +- .../AspireApp1.AppHost.csproj | 4 +- .../AspireApp1.ServiceDefaults.csproj | 2 +- .../AspireApp1.Tests/AspireApp1.Tests.csproj | 2 +- .../AspireApp1.Web/AspireApp1.Web.csproj | 2 +- .../VolumeMounts.AppHost.csproj | 10 +- .../VolumeMounts.ServiceDefaults.csproj | 2 +- .../build-aspire-apps-with-nodejs.md | 4 +- .../build-aspire-apps-with-python.md | 211 ++++++++++++++++++ docs/get-started/media/python-dashboard.png | Bin 0 -> 37570 bytes docs/get-started/media/python-hello-world.png | Bin 0 -> 8732 bytes .../media/python-telemetry-in-dashboard.png | Bin 0 -> 47387 bytes .../PythonSample.AppHost/Program.cs | 8 + .../Properties/launchSettings.json | 18 ++ .../PythonSample.AppHost.csproj | 17 ++ .../appsettings.Development.json | 8 + .../PythonSample.AppHost/appsettings.json | 9 + .../Extensions.cs | 111 +++++++++ .../PythonSample.ServiceDefaults.csproj | 22 ++ .../snippets/PythonSample/PythonSample.sln | 30 +++ .../PythonSample/hello-python/main.py | 17 ++ .../hello-python/requirements.txt | 2 + .../AspireSample.AppHost.csproj | 4 +- .../AspireSample.ServiceDefaults.csproj | 2 +- .../AspireSample.Web/AspireSample.Web.csproj | 2 +- docs/index.yml | 12 + docs/messaging/azure-event-hubs-component.md | 14 +- docs/messaging/azure-web-pubsub-component.md | 198 ++++++++++++++++ .../SignalR.AppHost/SignalR.AppHost.csproj | 6 +- .../SignalR.ServiceDefaults.csproj | 2 +- docs/search/elasticsearch-component.md | 202 +++++++++++++++++ .../AspireStorage.AppHost.csproj | 4 +- .../AspireStorage.ServiceDefaults.csproj | 2 +- .../AspireStorage.Web.csproj | 4 +- .../AspireStorage.WorkerService.csproj | 2 +- docs/toc.yml | 36 ++- docs/zones/zone-pivot-groups.yml | 11 +- 87 files changed, 1611 insertions(+), 123 deletions(-) create mode 100644 docs/app-host/withdockerfile.md create mode 100644 docs/authentication/keycloak-component.md create mode 100644 docs/caching/includes/garnet-app-host.md create mode 100644 docs/caching/includes/valkey-app-host.md create mode 100644 docs/database/milvus-component.md create mode 100644 docs/fundamentals/media/icons/AzureWebPubSub_256x.png create mode 100644 docs/fundamentals/media/icons/Elastic_logo_256x.png create mode 100644 docs/fundamentals/media/icons/Milvus_256x.png create mode 100644 docs/get-started/build-aspire-apps-with-python.md create mode 100644 docs/get-started/media/python-dashboard.png create mode 100644 docs/get-started/media/python-hello-world.png create mode 100644 docs/get-started/media/python-telemetry-in-dashboard.png create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.AppHost/Program.cs create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.AppHost/Properties/launchSettings.json create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.AppHost/PythonSample.AppHost.csproj create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.Development.json create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.json create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/Extensions.cs create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/PythonSample.ServiceDefaults.csproj create mode 100644 docs/get-started/snippets/PythonSample/PythonSample.sln create mode 100644 docs/get-started/snippets/PythonSample/hello-python/main.py create mode 100644 docs/get-started/snippets/PythonSample/hello-python/requirements.txt create mode 100644 docs/messaging/azure-web-pubsub-component.md create mode 100644 docs/search/elasticsearch-component.md diff --git a/.gitignore b/.gitignore index b4855007af..c70ca92e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Python virtual environments +.venv + docs/_build/ .idea/ *.swp diff --git a/docfx.json b/docfx.json index a824cea8f7..b583af4fba 100644 --- a/docfx.json +++ b/docfx.json @@ -149,10 +149,12 @@ "Docker", "Dockerfile", "EF Core", + "Elasticsearch", "Entity Framework Core", "Git", "GitHub", "JSON", + "Keycloak", "Kubernetes", "Linux", "localhost", diff --git a/docs/app-host/withdockerfile.md b/docs/app-host/withdockerfile.md new file mode 100644 index 0000000000..6f67a0f7a5 --- /dev/null +++ b/docs/app-host/withdockerfile.md @@ -0,0 +1,115 @@ +--- +title: Add Dockerfiles to your .NET app model +description: Learn how to add Dockerfiles to your .NET app model. +ms.date: 07/23/2024 +--- + +# Add Dockerfiles to your .NET app model + +With .NET Aspire it's possible to specify a _Dockerfile_ to build when the [app host](../fundamentals/app-host-overview.md) is started using either the or extension methods. + +## Add a Dockerfile to the app model + +In the following example the extension method is used to specify a container by referencing the context path for the container build. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var container = builder.AddDockerfile( + "mycontainer", "relative/context/path"); +``` + +Unless the context path argument is a rooted path the context path is interpreted as being relative to the app host projects directory (where the AppHost `*.csproj` folder is located). + +By default the name of the _Dockerfile_ which is used is `Dockerfile` and is expected to be within the context path directory. It's possible to explicitly specify the _Dockerfile_ name either as an absolute path or a relative path to the context path. + +This is useful if you wish to modify the specific _Dockerfile_ being used when running locally or when the app host is deploying. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var container = builder.ExecutionContext.IsRunMode + ? builder.AddDockerfile( + "mycontainer", "relative/context/path", "Dockerfile.debug") + : builder.AddDockerfile( + "mycontainer", "relative/context/path", "Dockerfile.release"); +``` + +## Customize existing container resources + +When using the return value is an `IResourceBuilder`. .NET Aspire includes many custom resource types that are derived from . + +Using the extension method it's possible to continue using these strongly typed resource types and customize the underlying container that is used. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var pgsql = builder.AddPostgres("pgsql") + .WithDockerfile("path/to/context") + .WithPgAdmin(); +``` + +## Pass build arguments + +The method can be used to pass arguments into the container image build. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var container = builder.AddDockerfile("mygoapp", "relative/context/path") + .WithBuildArg("GO_VERSION", "1.22"); +``` + +The value parameter on the method can be a literal value (`boolean`, `string`, `int`) or it can be a resource builder for a [parameter resource](../fundamentals/external-parameters.md). The following code replaces the `GO_VERSION` with a parameter value that can be specified at deployment time. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var goVersion = builder.AddParameter("goversion"); + +var container = builder.AddDockerfile("mygoapp", "relative/context/path") + .WithBuildArg("GO_VERSION", goVersion); +``` + +Build arguments correspond to the [`ARG` command](https://docs.docker.com/build/guide/build-args/) in _Dockerfiles_. Expanding the preceding example, this is a multi-stage _Dockerfile_ which specifies specific container image version to use as a parameter. + +```dockerfile +# Stage 1: Build the Go program +ARG GO_VERSION=1.22 +FROM golang:${GO_VERSION} AS builder +WORKDIR /build +COPY . . +RUN go build mygoapp.go + +# Stage 2: Run the Go program +FROM mcr.microsoft.com/cbl-mariner/base/core:2.0 +WORKDIR /app +COPY --from=builder /build/mygoapp . +CMD ["./mygoapp"] +``` + +> [!NOTE] +> Instead of hardcoding values into the container image, it's recommended to use environment variables for values that frequently change. This avoids the need to rebuild the container image whenever a change is required. + +## Pass build secrets + +In addition to build arguments it's possible to specify build secrets using which are made selectively available to individual commands in the _Dockerfile_ using the `--mount=type=secret` syntax on `RUN` commands. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var accessToken = builder.AddParameter("accesstoken", secret: true); + +var container = builder.AddDockerfile("myapp", "relative/context/path") + .WithBuildSecret("ACCESS_TOKEN", accessToken); +``` + +For example, consider the `RUN` command in a _Dockerfile_ which exposes the specified secret to the specific command: + +```dockerfile +# The helloworld command can read the secret from /run/secrets/ACCESS_TOKEN +RUN --mount=type=secret,id=ACCESS_TOKEN helloworld +``` + +> [!CAUTION] +> Caution should be exercised when passing secrets in build environments. This is often done when using a token to retrieve dependencies from private repositories or feeds before a build. It is important to ensure that the injected secrets are not copied into the final or intermediate images. diff --git a/docs/authentication/keycloak-component.md b/docs/authentication/keycloak-component.md new file mode 100644 index 0000000000..5e0fa9e581 --- /dev/null +++ b/docs/authentication/keycloak-component.md @@ -0,0 +1,134 @@ +--- +title: .NET Aspire Keycloak component +description: This article describes the .NET Aspire Keycloak component. +ms.topic: how-to +ms.date: 07/23/2024 +--- + +# .NET Aspire Keycloak component + +In this article, you learn how to use the .NET Aspire Keycloak component. The `Aspire.Keycloak.Authentication` library registers JwtBearer and OpenId Connect authentication handlers in the DI container for connecting to a Keycloak server. + +## Prerequisites + +- A Keycloak server instance. +- A Keycloak realm. +- For JwtBearer authentication, a configured audience in the Keycloak realm. +- For OpenId Connect authentication, the ID of a client configured in the Keycloak realm. + +## Get started + +To get started with the .NET Aspire Keycloak component, install the [Aspire.Keycloak.Authentication](https://www.nuget.org/packages/Aspire.Keycloak.Authentication) NuGet package in the consuming client project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Keycloak.Authentication +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). + +## Jwt bearer authentication usage example + +In the _Program.cs_ file of your ASP.NET Core API project, call the `AddKeycloakJwtBearer` extension method to add JwtBearer authentication, using a connection name, realm and any required JWT Bearer options: + +```csharp +builder.Services.AddAuthentication() + .AddKeycloakJwtBearer("keycloak", realm: "WeatherShop", options => + { + options.Audience = "weather.api"; + }); +``` + +You can set many other options via the `Action configureOptions` delegate. + +## OpenId Connect authentication usage example + +In the _Program.cs_ file of your Blazor project, call the `AddKeycloakOpenIdConnect` extension method to add OpenId Connect authentication, using a connection name, realm and any required OpenId Connect options: + +```csharp +builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddKeycloakOpenIdConnect( + "keycloak", + realm: "WeatherShop", + options => + { + options.ClientId = "WeatherWeb"; + options.ResponseType = OpenIdConnectResponseType.Code; + options.Scope.Add("weather:all"); + }); +``` + +You can set many other options via the `Action? configureOptions` delegate. + +## App host usage + +To model the Keycloak resource in the app host, install the [Aspire.Hosting.Keycloak](https://www.nuget.org/packages/Aspire.Hosting.Keycloak) NuGet package in the [app host](xref:aspire/app-host) project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Keycloak +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +Then, in the _Program.cs_ file of `AppHost`, register a Keycloak server and consume the connection using the following methods: + +```csharp +var keycloak = builder.AddKeycloak("keycloak", 8080); + +var apiService = builder.AddProject("apiservice") + .WithReference(keycloak); + +builder.AddProject("webfrontend") + .WithExternalHttpEndpoints() + .WithReference(keycloak) + .WithReference(apiService); +``` + +> [!TIP] +> For local development use a stable port for the Keycloak resource (8080 in the example above). It can be any port, but it should be stable to avoid issues with browser cookies that will persist OIDC tokens (which include the authority URL, with port) beyond the lifetime of the _app host_. + +The `WithReference` method configures a connection in the `Keycloak.ApiService` and `Keycloak.Web` projects named `keycloak`. + +In the _Program.cs_ file of `Keycloak.ApiService`, the Keycloak connection can be consumed using: + +```csharp +builder.Services.AddAuthentication() + .AddKeycloakJwtBearer("keycloak", realm: "WeatherShop"); +``` + +In the _Program.cs_ file of `Keycloak.Web`, the Keycloak connection can be consumed using: + +```csharp +var oidcScheme = OpenIdConnectDefaults.AuthenticationScheme; + +builder.Services.AddAuthentication(oidcScheme) + .AddKeycloakOpenIdConnect( + "keycloak", + realm: "WeatherShop", + oidcScheme); +``` + +## See also + +- [Keycloak](https://www.keycloak.org/) +- [.NET Aspire components](../fundamentals/components-overview.md) +- [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) diff --git a/docs/caching/includes/garnet-app-host.md b/docs/caching/includes/garnet-app-host.md new file mode 100644 index 0000000000..30cada3c66 --- /dev/null +++ b/docs/caching/includes/garnet-app-host.md @@ -0,0 +1,33 @@ +To model the Garnet resource (`GarnetResource`) in the app host, install the [Aspire.Hosting.Garnet](https://www.nuget.org/packages/Aspire.Hosting.Garnet) NuGet package in the [app host](xref:aspire/app-host) project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Garnet +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +In your app host project, register .NET Aspire Garnet as a `GarnetResource` using the `AddGarnet` method and consume the service using the following methods: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddGarnet("cache"); + +builder.AddProject() + .WithReference(cache) +``` + +The method configures a connection in the `ExampleProject` project named `cache`. In the _:::no-loc text="Program.cs":::_ file of `ExampleProject`, the Garnet connection can be consumed using: + +```csharp +builder.AddGarnet("cache"); +``` diff --git a/docs/caching/includes/redis-app-host.md b/docs/caching/includes/redis-app-host.md index a6c5929774..6dbe7074a4 100644 --- a/docs/caching/includes/redis-app-host.md +++ b/docs/caching/includes/redis-app-host.md @@ -1,4 +1,4 @@ -To model the Redis resource in the app host, install the [Aspire.Hosting.Redis](https://www.nuget.org/packages/Aspire.Hosting.Redis) NuGet package in the [app host](xref:aspire/app-host) project. +To model the Redis resource (`RedisResource`) in the app host, install the [Aspire.Hosting.Redis](https://www.nuget.org/packages/Aspire.Hosting.Redis) NuGet package in the [app host](xref:aspire/app-host) project. ### [.NET CLI](#tab/dotnet-cli) @@ -16,3 +16,18 @@ dotnet add package Aspire.Hosting.Redis --- In your app host project, register the .NET Aspire Stack Exchange Redis as a resource using the method and consume the service using the following methods: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddRedis("cache"); + +builder.AddProject() + .WithReference(cache) +``` + +The method configures a connection in the `ExampleProject` project named `cache`. In the _:::no-loc text="Program.cs":::_ file of `ExampleProject`, the Redis connection can be consumed using: + +```csharp +builder.AddRedis("cache"); +``` diff --git a/docs/caching/includes/valkey-app-host.md b/docs/caching/includes/valkey-app-host.md new file mode 100644 index 0000000000..ca44e5b488 --- /dev/null +++ b/docs/caching/includes/valkey-app-host.md @@ -0,0 +1,33 @@ +To model the Valkey resource (`ValkeyResource`) in the app host, install the [Aspire.Hosting.Valkey](https://www.nuget.org/packages/Aspire.Hosting.Valkey) NuGet package in the [app host](xref:aspire/app-host) project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Valkey +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +In your app host project, register the .NET Aspire Valkey as a resource using the `AddValkey` method and consume the service using the following methods: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddValkey("cache"); + +builder.AddProject() + .WithReference(cache) +``` + +The method configures a connection in the `ExampleProject` project named `cache`. In the _:::no-loc text="Program.cs":::_ file of `ExampleProject`, the Valkey connection can be consumed using: + +```csharp +builder.AddValkey("cache"); +``` diff --git a/docs/caching/stackexchange-redis-caching-overview.md b/docs/caching/stackexchange-redis-caching-overview.md index 242a464570..11b859bb59 100644 --- a/docs/caching/stackexchange-redis-caching-overview.md +++ b/docs/caching/stackexchange-redis-caching-overview.md @@ -1,7 +1,7 @@ --- title: Stack Exchange Redis caching overview description: Learn about Stack Exchange Redis caching and how to use it in your applications. -ms.date: 06/11/2024 +ms.date: 07/23/2024 --- # Stack Exchange Redis caching overview @@ -10,6 +10,17 @@ With .NET Aspire, there are several ways to use caching in your applications. On To use multiple Redis caching components in your application, see [Tutorial: Implement caching with .NET Aspire components](caching-components.md). If you're interested in using the Redis Cache for Azure, see [Tutorial: Deploy a .NET Aspire project with a Redis Cache to Azure](caching-components-deployment.md). +## Redis serialization protocol (RESP) + +The Redis serialization protocol (RESP) is a binary-safe protocol that Redis uses to communicate with clients. RESP is a simple, text-based protocol that is easy to implement and efficient to parse. RESP is used to send commands to Redis and receive responses from Redis. RESP is designed to be fast and efficient, making it well-suited for use in high-performance applications. For more information, see [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec/). + +In addition to Redis itself, there are two well-maintained implementations of RESP for .NET: + +- [Garnet](https://github.com/microsoft/Garnet): Garnet is a remote cache-store from Microsoft Research that offers strong performance (throughput and latency), scalability, storage, recovery, cluster sharding, key migration, and replication features. Garnet can work with existing Redis clients. +- [Valkey](https://github.com/valkey-io/valkey): A flexible distributed key-value datastore that supports both caching and beyond caching workloads. + +.NET Aspire lets you easily model either the Redis, Garnet, or Valkey RESP protocol in your applications and you can choose which one to use based on your requirements. All of the .NET Aspire Redis components can be used with either the Redis, Garnet, or Valkey RESP protocol. + ## Caching Caching is a technique used to store frequently accessed data in memory. This helps to reduce the time it takes to retrieve the data from the original source, such as a database or a web service. Caching can significantly improve the performance of an application by reducing the number of requests made to the original source. To access the Redis `IConnectionMultiplexer` object, you use the `Aspire.StackExchange.Redis` NuGet package: @@ -17,6 +28,12 @@ Caching is a technique used to store frequently accessed data in memory. This he > [!div class="nextstepaction"] > [.NET Aspire Stack Exchange Redis component](stackexchange-redis-component.md) +> [!div class="nextstepaction"] +> [.NET Aspire Stack Exchange Redis component (Garnet)](stackexchange-redis-component.md?pivots=garnet) + +> [!div class="nextstepaction"] +> [.NET Aspire Stack Exchange Redis component (Valkey)](stackexchange-redis-component.md?pivots=valkey) + ## Distributed caching Distributed caching is a type of caching that stores data across multiple servers. This allows the data to be shared between multiple instances of an application, which can help to improve scalability and performance. Distributed caching can be used to store a wide variety of data, such as session state, user profiles, and frequently accessed data. To use Redis distributed caching in your application (the `IDistributedCache` interface), use the `Aspire.StackExchange.Redis.DistributedCaching` NuGet package: @@ -24,6 +41,12 @@ Distributed caching is a type of caching that stores data across multiple server > [!div class="nextstepaction"] > [.NET Aspire Stack Exchange Redis distributed caching component](stackexchange-redis-distributed-caching-component.md) +> [!div class="nextstepaction"] +> [.NET Aspire Stack Exchange Redis distributed caching component (Garnet)](stackexchange-redis-distributed-caching-component.md?pivots=garnet) + +> [!div class="nextstepaction"] +> [.NET Aspire Stack Exchange Redis distributed caching component (Valkey)](stackexchange-redis-distributed-caching-component.md?pivots=valkey) + ## Output caching Output caching is a type of caching that stores the output of a web page or API response. This allows the response to be served directly from the cache, rather than generating it from scratch each time. Output caching can help to improve the performance of a web application by reducing the time it takes to generate a response. To use declarative Redis output caching with either the `OutputCache` attribute or the `CacheOutput` method in your application, use the `Aspire.StackExchange.Redis.OutputCaching` NuGet package: @@ -31,6 +54,12 @@ Output caching is a type of caching that stores the output of a web page or API > [!div class="nextstepaction"] > [.NET Aspire Stack Exchange Redis output caching component](stackexchange-redis-output-caching-component.md) +> [!div class="nextstepaction"] +> [.NET Aspire Stack Exchange Redis output caching component (Garnet)](stackexchange-redis-output-caching-component.md?pivots=garnet) + +> [!div class="nextstepaction"] +> [.NET Aspire Stack Exchange Redis output caching component (Valkey)](stackexchange-redis-output-caching-component.md?pivots=valkey) + ## See also - [Caching in .NET](/dotnet/core/extensions/caching) diff --git a/docs/caching/stackexchange-redis-component.md b/docs/caching/stackexchange-redis-component.md index 9af494eadf..cd95bb8e6a 100644 --- a/docs/caching/stackexchange-redis-component.md +++ b/docs/caching/stackexchange-redis-component.md @@ -2,7 +2,8 @@ title: .NET Aspire Stack Exchange Redis component description: This article describes the .NET Aspire Stack Exchange Redis component features and capabilities ms.topic: how-to -ms.date: 07/17/2024 +ms.date: 07/23/2024 +zone_pivot_groups: resp-host --- # .NET Aspire Stack Exchange Redis component @@ -49,22 +50,21 @@ public class ExampleService(IConnectionMultiplexer connectionMultiplexer) ## App host usage +:::zone pivot="redis" + [!INCLUDE [redis-app-host](includes/redis-app-host.md)] -```csharp -var builder = DistributedApplication.CreateBuilder(args); +:::zone-end +:::zone pivot="garnet" -var redis = builder.AddRedis("redis"); +[!INCLUDE [garnet-app-host](includes/garnet-app-host.md)] -builder.AddProject() - .WithReference(redis) -``` +:::zone-end +:::zone pivot="valkey" -The method configures a connection in the `ExampleProject` project named `redis`. In the _:::no-loc text="Program.cs":::_ file of `ExampleProject`, the Redis connection can be consumed using: +[!INCLUDE [valkey-app-host](includes/valkey-app-host.md)] -```csharp -builder.AddRedis("cache"); -``` +:::zone-end ## Configuration @@ -72,18 +72,42 @@ The .NET Aspire Stack Exchange Redis component provides multiple options to conf ### Use a connection string +:::zone pivot="redis" + When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling `builder.AddRedis`: ```csharp -builder.AddRedis("RedisConnection"); +builder.AddRedis("cache"); +``` + +:::zone-end +:::zone pivot="garnet" + +When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling `builder.AddGarnet`: + +```csharp +builder.AddGarnet("cache"); ``` +:::zone-end +:::zone pivot="valkey" + +[!INCLUDE [valkey-app-host](includes/valkey-app-host.md)] + +When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling `builder.AddValkey`: + +```csharp +builder.AddValkey("cache"); +``` + +:::zone-end + And then the connection string will be retrieved from the `ConnectionStrings` configuration section: ```json { "ConnectionStrings": { - "RedisConnection": "localhost:6379" + "cache": "localhost:6379" } } ``` @@ -115,12 +139,36 @@ The .NET Aspire Stack Exchange Redis component supports ` delegate to set up some or all the options inline, for example to configure `DisableTracing`: +:::zone pivot="redis" + ```csharp builder.AddRedis( "cache", settings => settings.DisableTracing = true); ``` +:::zone-end +:::zone pivot="garnet" + +```csharp +builder.AddGarnet( + "cache", + settings => settings.DisableTracing = true); +``` + +:::zone-end +:::zone pivot="valkey" + +[!INCLUDE [valkey-app-host](includes/valkey-app-host.md)] + +```csharp +builder.AddValkey( + "cache", + settings => settings.DisableTracing = true); +``` + +:::zone-end + [!INCLUDE [component-health-checks](../includes/component-health-checks.md)] The .NET Aspire Stack Exchange Redis component handles the following: diff --git a/docs/caching/stackexchange-redis-distributed-caching-component.md b/docs/caching/stackexchange-redis-distributed-caching-component.md index 42df645d52..37536bb4bd 100644 --- a/docs/caching/stackexchange-redis-distributed-caching-component.md +++ b/docs/caching/stackexchange-redis-distributed-caching-component.md @@ -2,7 +2,8 @@ title: .NET Aspire Stack Exchange Redis distributed caching component description: This article describes the .NET Aspire Stack Exchange Redis distributed caching component features and capabilities ms.topic: how-to -ms.date: 07/17/2024 +ms.date: 07/23/2024 +zone_pivot_groups: resp-host --- # .NET Aspire Stack Exchange Redis distributed caching component @@ -49,22 +50,21 @@ public class ExampleService(IDistributedCache cache) ## App host usage +:::zone pivot="redis" + [!INCLUDE [redis-app-host](includes/redis-app-host.md)] -```csharp -var builder = DistributedApplication.CreateBuilder(args); +:::zone-end +:::zone pivot="garnet" -var redis = builder.AddRedis("cache"); +[!INCLUDE [garnet-app-host](includes/garnet-app-host.md)] -builder.AddProject() - .WithReference(redis) -``` +:::zone-end +:::zone pivot="valkey" -The method configures a connection in the `ExampleProject` project named `cache`. In the _:::no-loc text="Program.cs":::_ file of `ExampleProject`, the Redis connection can be consumed using: +[!INCLUDE [valkey-app-host](includes/valkey-app-host.md)] -```csharp -builder.AddRedisDistributedCache("cache"); -``` +:::zone-end ## Configuration diff --git a/docs/caching/stackexchange-redis-output-caching-component.md b/docs/caching/stackexchange-redis-output-caching-component.md index c7c24d9915..9fd3e6fe24 100644 --- a/docs/caching/stackexchange-redis-output-caching-component.md +++ b/docs/caching/stackexchange-redis-output-caching-component.md @@ -2,12 +2,13 @@ title: .NET Aspire Stack Exchange Redis output caching Component description: This article describes the .NET Aspire Stack Exchange Redis output caching component features and capabilities ms.topic: how-to -ms.date: 07/17/2024 +ms.date: 07/23/2024 +zone_pivot_groups: resp-host --- # .NET Aspire Stack Exchange Redis output caching component -In this article, you learn how to use the .NET Aspire Stack Exchange Redis output caching component. The `Aspire.StackExchange.Redis.OutputCaching` library is used to register an [ASP.NET Core Output Caching](/aspnet/core/performance/caching/output) provider backed by a [Redis](https://redis.io/) server. It enables corresponding health check, logging, and telemetry. +In this article, you learn how to use the .NET Aspire Stack Exchange Redis output caching component. The `Aspire.StackExchange.Redis.OutputCaching` library is used to register an [ASP.NET Core Output Caching](/aspnet/core/performance/caching/output) provider backed by a [Redis](https://redis.io/) server. It enables corresponding health check, logging, and telemetry.. ## Get started @@ -56,22 +57,21 @@ For apps with controllers, apply the `[OutputCache]` attribute to the action met ## App host usage +:::zone pivot="redis" + [!INCLUDE [redis-app-host](includes/redis-app-host.md)] -```csharp -var builder = DistributedApplication.CreateBuilder(args); +:::zone-end +:::zone pivot="garnet" -var redis = builder.AddRedis("redis"); +[!INCLUDE [garnet-app-host](includes/garnet-app-host.md)] -var basket = builder.AddProject() - .WithReference(redis) -``` +:::zone-end +:::zone pivot="valkey" -The method configures a connection in the `ExampleProject` project named `redis`. In the _:::no-loc text="Program.cs":::_ file of `ExampleProject`, the Redis connection can be consumed using: +[!INCLUDE [valkey-app-host](includes/valkey-app-host.md)] -```csharp -builder.AddRedisOutputCache("messaging"); -``` +:::zone-end ## Configuration diff --git a/docs/database/milvus-component.md b/docs/database/milvus-component.md new file mode 100644 index 0000000000..1bcb83b4b2 --- /dev/null +++ b/docs/database/milvus-component.md @@ -0,0 +1,162 @@ +--- +title: .NET Aspire Milvus database component +description: This article describes the .NET Aspire Milvus database component. +ms.topic: how-to +ms.date: 07/23/2024 +--- + +# .NET Aspire Milvus database component + +In this article, you learn how to use the .NET Aspire Milvus database component. The `Aspire.Milvus.Client` library registers a [MilvusClient](https://github.com/milvus-io/milvus-sdk-csharp) in the DI container for connecting to a Milvus server. + +## Prerequisites + +- Milvus server and connection string for accessing the server API endpoint. + +## Get started + +To get started with the .NET Aspire Milvus database component, install the [Aspire.Milvus.Client](https://www.nuget.org/packages/Aspire.Milvus.Client) NuGet package in the consuming client project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Milvus.Client +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). + +## Example usage + +In the _Program.cs_ file of your project, call the `AddMilvusClient` extension method to register a `MilvusClient` for use via the dependency injection container. The method takes a connection name parameter. + +```csharp +builder.AddMilvusClient("milvus"); +``` + +## App host usage + +To model the Milvus resource in the app host, install the [Aspire.Hosting.Milvus](https://www.nuget.org/packages/Aspire.Hosting.Milvus) NuGet package in the [app host](xref:aspire/app-host) project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Milvus +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +In the _Program.cs_ file of `AppHost`, register a Milvus server and consume the connection using the following methods: + +```csharp +var milvus = builder.AddMilvus("milvus"); + +var myService = builder.AddProject() + .WithReference(milvus); +``` + +The `WithReference` method configures a connection in the `MyService` project named `milvus`. In the _Program.cs_ file of `MyService`, the Milvus connection can be consumed using: + +```csharp +builder.AddMilvusClient("milvus"); +``` + +## Configuration + +The .NET Aspire Milvus Client component provides multiple options to configure the server connection based on the requirements and conventions of your project. + +> [!TIP] +> The default use is `root` and the default password is `Milvus`. Currently, Milvus doesn't support changing the superuser password at startup. It needs to be manually changed with the client. + +### Use a connection string + +When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling `builder.AddMilvusClient()`: + +```csharp +builder.AddMilvusClient("milvus"); +``` + +And then the connection string will be retrieved from the `ConnectionStrings` configuration section: + +```json +{ + "ConnectionStrings": { + "milvus": "Endpoint=http://localhost:19530/;Key=root:123456!@#$%" + } +} +``` + +By default the `MilvusClient` uses the gRPC API endpoint. + +### Use configuration providers + +The .NET Aspire Milvus Client component supports [Microsoft.Extensions.Configuration](/dotnet/api/microsoft.extensions.configuration). It loads the `MilvusSettings` from configuration by using the `Aspire:Milvus:Client` key. Consider the following example _appsettings.json_ that configures some of the options: + +```json +{ + "Aspire": { + "Milvus": { + "Client": { + "Key": "root:123456!@#$%" + } + } + } +} +``` + +### Use inline delegates + +Also you can pass the `Action configureSettings` delegate to set up some or all the options inline, for example to set the API key from code: + +```csharp +builder.AddMilvusClient( + "milvus", + settings => settings.Key = "root:12345!@#$%"); +``` + +[!INCLUDE [component-health-checks](../includes/component-health-checks.md)] + +The .NET Aspire Milvus database component uses the configured client to perform a `HealthAsync`. If the result _is healthy_, the health check is considered healthy, otherwise it's unhealthy. Likewise, if there's an exception, the health check is considered unhealthy with the error propagating through the health check failure. + +[!INCLUDE [component-observability-and-telemetry](../includes/component-observability-and-telemetry.md)] + +### Logging + +The .NET Aspire Milvus database component uses standard .NET logging, and you'll see log entries from the following category: + +- `Milvus.Client` + +## See also + +- [Milvus .NET SDK](https://github.com/milvus-io/milvus-sdk-csharp) +- [.NET Aspire components](../fundamentals/components-overview.md) +- [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) + + \ No newline at end of file diff --git a/docs/database/snippets/cosmos-db/AspireApp.ApiService/AspireApp.ApiService.csproj b/docs/database/snippets/cosmos-db/AspireApp.ApiService/AspireApp.ApiService.csproj index e9bed540ab..b39697686c 100644 --- a/docs/database/snippets/cosmos-db/AspireApp.ApiService/AspireApp.ApiService.csproj +++ b/docs/database/snippets/cosmos-db/AspireApp.ApiService/AspireApp.ApiService.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/database/snippets/cosmos-db/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/database/snippets/cosmos-db/AspireApp.AppHost/AspireApp.AppHost.csproj index 16fb05970d..d8e52cccdc 100644 --- a/docs/database/snippets/cosmos-db/AspireApp.AppHost/AspireApp.AppHost.csproj +++ b/docs/database/snippets/cosmos-db/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/docs/database/snippets/cosmos-db/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/database/snippets/cosmos-db/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/database/snippets/cosmos-db/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj +++ b/docs/database/snippets/cosmos-db/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.AppHost/AspireSql.AppHost.csproj b/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.AppHost/AspireSql.AppHost.csproj index c6e85315d4..ff35507796 100644 --- a/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.AppHost/AspireSql.AppHost.csproj +++ b/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.AppHost/AspireSql.AppHost.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj b/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj +++ b/docs/database/snippets/tutorial/aspiresqldeployazure/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.AppHost/AspireSql.AppHost.csproj b/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.AppHost/AspireSql.AppHost.csproj index ab28716e81..008a649bdc 100644 --- a/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.AppHost/AspireSql.AppHost.csproj +++ b/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.AppHost/AspireSql.AppHost.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj b/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj +++ b/docs/database/snippets/tutorial/aspiresqldeploycontainer/AspireSql.ServiceDefaults/AspireSql.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.AppHost/AspireSQLEFCore.AppHost.csproj b/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.AppHost/AspireSQLEFCore.AppHost.csproj index 2a3433bf1a..0568378087 100644 --- a/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.AppHost/AspireSQLEFCore.AppHost.csproj +++ b/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.AppHost/AspireSQLEFCore.AppHost.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.ServiceDefaults/AspireSQLEFCore.ServiceDefaults.csproj b/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.ServiceDefaults/AspireSQLEFCore.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.ServiceDefaults/AspireSQLEFCore.ServiceDefaults.csproj +++ b/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore.ServiceDefaults/AspireSQLEFCore.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore/AspireSQLEFCore.csproj b/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore/AspireSQLEFCore.csproj index cd6d54683e..b1e8d92531 100644 --- a/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore/AspireSQLEFCore.csproj +++ b/docs/database/snippets/tutorial/aspiresqlefcore/AspireSQLEFCore/AspireSQLEFCore.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/deployment/azure/snippets/AppHost.Bicep/AppHost.Bicep.csproj b/docs/deployment/azure/snippets/AppHost.Bicep/AppHost.Bicep.csproj index c9dee9a8d1..3ec3d0b836 100644 --- a/docs/deployment/azure/snippets/AppHost.Bicep/AppHost.Bicep.csproj +++ b/docs/deployment/azure/snippets/AppHost.Bicep/AppHost.Bicep.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDev.Hosting.csproj b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDev.Hosting.csproj index fc602e5bc4..1230adeafe 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDev.Hosting.csproj +++ b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDev.Hosting.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/MailDevResource.AppHost.csproj b/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/MailDevResource.AppHost.csproj index 4e177d9495..10ac6c5b34 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/MailDevResource.AppHost.csproj +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/MailDevResource.AppHost.csproj @@ -10,7 +10,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj b/docs/extensibility/snippets/MailDevResource/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj index fc602e5bc4..1230adeafe 100644 --- a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj index 4e177d9495..10ac6c5b34 100644 --- a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj @@ -10,7 +10,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKit.Client.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKit.Client.csproj index c543c8dbe9..f4b0cfa7a5 100644 --- a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKit.Client.csproj +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKit.Client.csproj @@ -8,7 +8,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj index fc602e5bc4..1230adeafe 100644 --- a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj index 4e177d9495..10ac6c5b34 100644 --- a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj @@ -10,7 +10,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj index c543c8dbe9..f4b0cfa7a5 100644 --- a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj @@ -8,7 +8,7 @@ - + diff --git a/docs/frameworks/snippets/Dapr/Dapr.AppHost/Dapr.AppHost.csproj b/docs/frameworks/snippets/Dapr/Dapr.AppHost/Dapr.AppHost.csproj index 33a49c0a85..b29cd0e949 100644 --- a/docs/frameworks/snippets/Dapr/Dapr.AppHost/Dapr.AppHost.csproj +++ b/docs/frameworks/snippets/Dapr/Dapr.AppHost/Dapr.AppHost.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/docs/frameworks/snippets/Dapr/Dapr.ServiceDefaults/Dapr.ServiceDefaults.csproj b/docs/frameworks/snippets/Dapr/Dapr.ServiceDefaults/Dapr.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/frameworks/snippets/Dapr/Dapr.ServiceDefaults/Dapr.ServiceDefaults.csproj +++ b/docs/frameworks/snippets/Dapr/Dapr.ServiceDefaults/Dapr.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/frameworks/snippets/Orleans/OrleansAppHost/OrleansAppHost.csproj b/docs/frameworks/snippets/Orleans/OrleansAppHost/OrleansAppHost.csproj index f12ff38d42..c4f4e52fbc 100644 --- a/docs/frameworks/snippets/Orleans/OrleansAppHost/OrleansAppHost.csproj +++ b/docs/frameworks/snippets/Orleans/OrleansAppHost/OrleansAppHost.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/docs/frameworks/snippets/Orleans/OrleansServiceDefaults/OrleansServiceDefaults.csproj b/docs/frameworks/snippets/Orleans/OrleansServiceDefaults/OrleansServiceDefaults.csproj index 3e0d94a75e..d39dbd4c83 100644 --- a/docs/frameworks/snippets/Orleans/OrleansServiceDefaults/OrleansServiceDefaults.csproj +++ b/docs/frameworks/snippets/Orleans/OrleansServiceDefaults/OrleansServiceDefaults.csproj @@ -15,7 +15,7 @@ - + diff --git a/docs/fundamentals/components-overview.md b/docs/fundamentals/components-overview.md index d3fa4be6af..f28b9fdba1 100644 --- a/docs/fundamentals/components-overview.md +++ b/docs/fundamentals/components-overview.md @@ -1,7 +1,7 @@ --- title: .NET Aspire components overview description: Explore the fundamental concepts of .NET Aspire components and learn how to integrate them into your apps. -ms.date: 05/23/2024 +ms.date: 07/23/2024 ms.topic: conceptual --- @@ -23,20 +23,24 @@ The following table lists the .NET Aspire components currently available for use |--|--|--| | [Apache Kafka](../messaging/kafka-component.md)
.NET Aspire logo. | [Aspire.Confluent.Kafka](https://www.nuget.org/packages/Aspire.Confluent.Kafka) | A library for producing and consuming messages from an [Apache Kafka](https://kafka.apache.org/) broker. | | [Azure AI OpenAI](../azureai/azureai-openai-component.md)
Azire OpenAI logo. | [Aspire.Azure.AI.OpenAI](https://www.nuget.org/packages/Aspire.Azure.AI.OpenAI) | A library for accessing [Azure AI OpenAI](/azure/ai-services/openai/overview) or OpenAI functionality. | -| [Azure Search Documents](../azureai/azureai-search-document-component.md)
Azure Search Documents logo. | [Aspire.Azure.Search.Documents](https://www.nuget.org/packages/Aspire.Azure.Search.Documents) | A library for accessing [Azure AI Search](/azure/search/search-what-is-azure-search). | | [Azure Blob Storage](../storage/azure-storage-blobs-component.md)
Azure Blog Storage logo. | [Aspire.Azure.Storage.Blobs](https://www.nuget.org/packages/Aspire.Azure.Storage.Blobs) | A library for accessing [Azure Blob Storage](/azure/storage/blobs/storage-blobs-introduction). | | [Azure Cosmos DB Entity Framework Core](../database/azure-cosmos-db-entity-framework-component.md)
Azure Cosmos DB EF logo. | [Aspire.Microsoft.EntityFrameworkCore.Cosmos](https://www.nuget.org/packages/Aspire.Microsoft.EntityFrameworkCore.Cosmos) | A library for accessing Azure Cosmos DB databases with [Entity Framework Core](/ef/core/providers/cosmos/). | | [Azure Cosmos DB](../database/azure-cosmos-db-component.md)
Azure Cosmos DB logo. | [Aspire.Microsoft.Azure.Cosmos](https://www.nuget.org/packages/Aspire.Microsoft.Azure.Cosmos) | A library for accessing [Azure Cosmos DB](/azure/cosmos-db/introduction) databases. | | [Azure Event Hubs](../messaging/azure-event-hubs-component.md)
Azure Event Hubs logo. | [Aspire.Azure.Messaging.EventHubs](https://www.nuget.org/packages/Aspire.Azure.Messaging.EventHubs) | A library for accessing [Azure Event Hubs](/azure/event-hubs/event-hubs-about). | | [Azure Key Vault](../security/azure-security-key-vault-component.md)
Azure Key Vault logo. | [Aspire.Azure.Security.KeyVault](https://www.nuget.org/packages/Aspire.Azure.Security.KeyVault) | A library for accessing [Azure Key Vault](/azure/key-vault/general/overview). | +| [Azure Search Documents](../azureai/azureai-search-document-component.md)
Azure Search Documents logo. | [Aspire.Azure.Search.Documents](https://www.nuget.org/packages/Aspire.Azure.Search.Documents) | A library for accessing [Azure AI Search](/azure/search/search-what-is-azure-search). | | [Azure Service Bus](../messaging/azure-service-bus-component.md)
Azure Service Bus logo. | [Aspire.Azure.Messaging.ServiceBus](https://www.nuget.org/packages/Aspire.Azure.Messaging.ServiceBus) | A library for accessing [Azure Service Bus](/azure/service-bus-messaging/service-bus-messaging-overview). | | [Azure Storage Queues](../storage/azure-storage-queues-component.md)
Azure Storage Queues logo. | [Aspire.Azure.Storage.Queues](https://www.nuget.org/packages/Aspire.Azure.Storage.Queues) | A library for accessing [Azure Storage Queues](/azure/storage/queues/storage-queues-introduction). | | [Azure Table Storage](../storage/azure-storage-tables-component.md)
Azure Table Storage logo. | [Aspire.Azure.Data.Tables](https://www.nuget.org/packages/Aspire.Azure.Data.Tables) | A library for accessing the [Azure Table](/azure/storage/tables/table-storage-overview) service. | +| [Azure Web PubSub](../messaging/azure-web-pubsub-component.md)
Azure Web PubSub logo. | [Aspire.Azure.Messaging.WebPubSub](https://www.nuget.org/packages/Aspire.Azure.Messaging.WebPubSub) | A library for accessing the [Azure Web PubSub](/azure/azure-web-pubsub/) service. | +| [Elasticsearch](../search/elasticsearch-component.md)
Elasticsearch logo. | [Aspire.Elasticsearch.Client](https://www.nuget.org/packages/Aspire.Elasticsearch.Client) | A library for accessing [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/client/index.html) databases. | +| [Keycloak](../authentication/keycloak-component.md)
.NET Aspire logo. | [Aspire.Keycloak.Authentication](https://www.nuget.org/packages/Aspire.Keycloak.Authentication) | A library for accessing [Keycloak](https://www.keycloak.org/docs/latest/server_admin/index.html) authentication. | +| [Milvus](../database/milvus-component.md)
Milvus logo. | [Aspire.Milvus.Client](https://www.nuget.org/packages/Aspire.Milvus.Client) | A library for accessing [Milvus](https://milvus.io/) databases. | | [MongoDB Driver](../database/mongodb-component.md)
MongoDB logo. | [Aspire.MongoDB.Driver](https://www.nuget.org/packages/Aspire.MongoDB.Driver) | A library for accessing [MongoDB](https://www.mongodb.com/docs) databases. | | [MySqlConnector](../database/mysql-component.md)
MySqlConnector logo. | [Aspire.MySqlConnector](https://www.nuget.org/packages/Aspire.MySqlConnector) | A library for accessing [MySqlConnector](https://mysqlconnector.net/) databases. | | [NATS](../messaging/nats-component.md)
NATS logo. | [Aspire.NATS.Net](https://www.nuget.org/packages/Aspire.NATS.Net) | A library for accessing [NATS](https://nats.io/) messaging. | -| [Pomelo MySQL Entity Framework Core](../database/mysql-entity-framework-component.md)
.NET Aspire logo. | [Aspire.Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Aspire.Pomelo.EntityFrameworkCore.MySql) | A library for accessing MySql databases with [Entity Framework Core](/ef/core). | | [Oracle Entity Framework Core](../database/oracle-entity-framework-component.md)
.NET Aspire logo. | [Aspire.Oracle.EntityFrameworkCore](https://www.nuget.org/packages/Aspire.Oracle.EntityFrameworkCore) | A library for accessing Oracle databases with [Entity Framework Core](/ef/core). | +| [Pomelo MySQL Entity Framework Core](../database/mysql-entity-framework-component.md)
.NET Aspire logo. | [Aspire.Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Aspire.Pomelo.EntityFrameworkCore.MySql) | A library for accessing MySql databases with [Entity Framework Core](/ef/core). | | [PostgreSQL Entity Framework Core](../database/postgresql-entity-framework-component.md)
PostgreSQL logo. | [Aspire.Npgsql.EntityFrameworkCore.PostgreSQL](https://www.nuget.org/packages/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL) | A library for accessing PostgreSQL databases using [Entity Framework Core](https://www.npgsql.org/efcore/index.html). | | [PostgreSQL](../database/postgresql-component.md)
PostgreSQL logo. | [Aspire.Npgsql](https://www.nuget.org/packages/Aspire.Npgsql) | A library for accessing [PostgreSQL](https://www.npgsql.org/doc/index.html) databases. | | [Qdrant](../database/qdrant-component.md)
Qdrant logo. | [Aspire.Qdrant.Client](https://www.nuget.org/packages/Aspire.Qdrant.Client) | A library for accessing [Qdrant](https://qdrant.tech/) databases. | diff --git a/docs/fundamentals/media/icons/AzureWebPubSub_256x.png b/docs/fundamentals/media/icons/AzureWebPubSub_256x.png new file mode 100644 index 0000000000000000000000000000000000000000..5625427aa42338c2ceb07b031c06a8d089a82078 GIT binary patch literal 10709 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 bA3>T;MAKhSk@To4Oh8%wja->5Ea?9Le(G|q literal 0 HcmV?d00001 diff --git a/docs/fundamentals/media/icons/Elastic_logo_256x.png b/docs/fundamentals/media/icons/Elastic_logo_256x.png new file mode 100644 index 0000000000000000000000000000000000000000..699ef0e0360c1cade17d36a21f92b8b128213922 GIT binary patch literal 20759 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�<krIjjQkgzD|J6GyfoijNbTzEKk$; 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 literal 0 HcmV?d00001 diff --git a/docs/fundamentals/snippets/components/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/fundamentals/snippets/components/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj index 4c183dc273..a9228e7213 100644 --- a/docs/fundamentals/snippets/components/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj +++ b/docs/fundamentals/snippets/components/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/docs/fundamentals/snippets/components/AspireApp/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/docs/fundamentals/snippets/components/AspireApp/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/fundamentals/snippets/components/AspireApp/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/components/AspireApp/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/fundamentals/snippets/healthz/Healthz.ServiceDefaults/Healthz.ServiceDefaults.csproj b/docs/fundamentals/snippets/healthz/Healthz.ServiceDefaults/Healthz.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/fundamentals/snippets/healthz/Healthz.ServiceDefaults/Healthz.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/healthz/Healthz.ServiceDefaults/Healthz.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/fundamentals/snippets/networking/Networking.AppHost/Networking.AppHost.csproj b/docs/fundamentals/snippets/networking/Networking.AppHost/Networking.AppHost.csproj index 1c8d96710e..4bb9e45d13 100644 --- a/docs/fundamentals/snippets/networking/Networking.AppHost/Networking.AppHost.csproj +++ b/docs/fundamentals/snippets/networking/Networking.AppHost/Networking.AppHost.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/docs/fundamentals/snippets/networking/Networking.ServiceDefaults/Networking.ServiceDefaults.csproj b/docs/fundamentals/snippets/networking/Networking.ServiceDefaults/Networking.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/fundamentals/snippets/networking/Networking.ServiceDefaults/Networking.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/networking/Networking.ServiceDefaults/Networking.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/fundamentals/snippets/params/Parameters.ApiService/Parameters.ApiService.csproj b/docs/fundamentals/snippets/params/Parameters.ApiService/Parameters.ApiService.csproj index 5af75a18e8..95da5e0531 100644 --- a/docs/fundamentals/snippets/params/Parameters.ApiService/Parameters.ApiService.csproj +++ b/docs/fundamentals/snippets/params/Parameters.ApiService/Parameters.ApiService.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/fundamentals/snippets/params/Parameters.AppHost/Parameters.AppHost.csproj b/docs/fundamentals/snippets/params/Parameters.AppHost/Parameters.AppHost.csproj index 4ebd8c8f6b..45e9e84d04 100644 --- a/docs/fundamentals/snippets/params/Parameters.AppHost/Parameters.AppHost.csproj +++ b/docs/fundamentals/snippets/params/Parameters.AppHost/Parameters.AppHost.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/docs/fundamentals/snippets/params/Parameters.ServiceDefaults/Parameters.ServiceDefaults.csproj b/docs/fundamentals/snippets/params/Parameters.ServiceDefaults/Parameters.ServiceDefaults.csproj index 23006b8b7b..94e10ec058 100644 --- a/docs/fundamentals/snippets/params/Parameters.ServiceDefaults/Parameters.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/params/Parameters.ServiceDefaults/Parameters.ServiceDefaults.csproj @@ -12,7 +12,7 @@ - + diff --git a/docs/fundamentals/snippets/template/YourAppName/YourAppName.ServiceDefaults.csproj b/docs/fundamentals/snippets/template/YourAppName/YourAppName.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/fundamentals/snippets/template/YourAppName/YourAppName.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/template/YourAppName/YourAppName.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.AppHost/AspireApp1.AppHost.csproj b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.AppHost/AspireApp1.AppHost.csproj index 65bd0d66ba..eac3bf3064 100644 --- a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.AppHost/AspireApp1.AppHost.csproj +++ b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.AppHost/AspireApp1.AppHost.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.ServiceDefaults/AspireApp1.ServiceDefaults.csproj b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.ServiceDefaults/AspireApp1.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.ServiceDefaults/AspireApp1.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.ServiceDefaults/AspireApp1.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Tests/AspireApp1.Tests.csproj b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Tests/AspireApp1.Tests.csproj index c4e26ea722..2894b0cd2b 100644 --- a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Tests/AspireApp1.Tests.csproj +++ b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Tests/AspireApp1.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Web/AspireApp1.Web.csproj b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Web/AspireApp1.Web.csproj index 57034996c4..344a1197dc 100644 --- a/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Web/AspireApp1.Web.csproj +++ b/docs/fundamentals/snippets/testing/AspireApp1/AspireApp1.Web/AspireApp1.Web.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/fundamentals/snippets/volumes/VolumeMounts.AppHost/VolumeMounts.AppHost.csproj b/docs/fundamentals/snippets/volumes/VolumeMounts.AppHost/VolumeMounts.AppHost.csproj index 9c3920bc03..30a7a83de1 100644 --- a/docs/fundamentals/snippets/volumes/VolumeMounts.AppHost/VolumeMounts.AppHost.csproj +++ b/docs/fundamentals/snippets/volumes/VolumeMounts.AppHost/VolumeMounts.AppHost.csproj @@ -15,11 +15,11 @@ - - - - - + + + + + diff --git a/docs/fundamentals/snippets/volumes/VolumeMounts.ServiceDefaults/VolumeMounts.ServiceDefaults.csproj b/docs/fundamentals/snippets/volumes/VolumeMounts.ServiceDefaults/VolumeMounts.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/fundamentals/snippets/volumes/VolumeMounts.ServiceDefaults/VolumeMounts.ServiceDefaults.csproj +++ b/docs/fundamentals/snippets/volumes/VolumeMounts.ServiceDefaults/VolumeMounts.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/get-started/build-aspire-apps-with-nodejs.md b/docs/get-started/build-aspire-apps-with-nodejs.md index 0302e611a8..d6e351529f 100644 --- a/docs/get-started/build-aspire-apps-with-nodejs.md +++ b/docs/get-started/build-aspire-apps-with-nodejs.md @@ -1,10 +1,10 @@ --- -title: Add Node.js apps to a .NET Aspire project +title: Orchestrate Node.js apps in .NET Aspire description: Learn how to integrate Node.js and npm apps into a .NET Aspire App Host project. ms.date: 06/13/2024 --- -# Add Node.js apps to a .NET Aspire project +# Orchestrate Node.js apps in .NET Aspire In this article, you learn how to use Node.js and Node Package Manager (`npm`) apps in a .NET Aspire project. The sample app in this article demonstrates [Angular](https://angular.io), [React](https://react.dev/), and [Vue](https://vuejs.org/) client experiences. The following .NET Aspire APIs exist to support these scenarios: diff --git a/docs/get-started/build-aspire-apps-with-python.md b/docs/get-started/build-aspire-apps-with-python.md new file mode 100644 index 0000000000..098a3fabcc --- /dev/null +++ b/docs/get-started/build-aspire-apps-with-python.md @@ -0,0 +1,211 @@ +--- +title: Orchestrate Python apps in .NET Aspire +description: Learn how to integrate Python apps into a .NET Aspire App Host project. +ms.date: 07/23/2024 +--- + +# Orchestrate Python apps in .NET Aspire + +In this article, you learn how to use Python apps in a .NET Aspire project. The sample app in this article demonstrates launching a Python application. The Python extension for .NET Aspire requires the use of virtual environments. + +[!INCLUDE [aspire-prereqs](../includes/aspire-prereqs.md)] + +Additionally, you need to install [Python](https://www.python.org/downloads) on your machine. The sample app in this article was built with Python version 3.12.4 and pip version 24.1.2. To verify your Python and pip versions, run the following commands: + +```python +python --version +``` + +```python +pip --version +``` + +To download Python (including `pip`), see the [Python download page](https://www.python.org/downloads). + +## Create a .NET Aspire project using the template + +To get started launching a Python project in .NET Aspire first use the starter template to create a .NET Aspire application host: + +```dotnetcli +dotnet new aspire -o PythonSample +``` + +In the same terminal session, change directories into the newly created project: + +```dotnetcli +cd PythonSample +``` + +Once the template has been created launch the app host with the following command to ensure that the app host and the [.NET Aspire dashboard](../fundamentals/dashboard/overview.md) launches successfully: + +```dotnetcli +dotnet run --project PythonSample.AppHost/PythonSample.AppHost.csproj +``` + +Once the app host starts it should be possible to click on the dashboard link in the console output. At this point the dashboard will not show any resources. Stop the app host by pressing Ctrl + C in the terminal. + +## Prepare a Python project + +From your previous terminal session where you created the .NET Aspire solution, create a new directory to contain the Python source code. + +```Console +mkdir hello-python +``` + +Change directories into the newly created _hello-python_ directory: + +```Console +cd hello-python +``` + +### Initialize the Python virtual environment + +To work with Python projects, they need to be within a virtual environment. To create a virtual environment, run the following command: + +```python +python -m venv .venv +``` + +For more information on virtual environments, see the [Python: Install packages in a virtual environment using pip and venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/). + +To activate the virtual environment, enabling installation and usage of packages, run the following command: + +### [Unix/macOS](#tab/bash) + +```bash +source .venv/bin/activate +``` + +### [Windows](#tab/powershell) + +```powershell +.venv\Scripts\Activate.ps1 +``` + +--- + +Ensure that pip within the virtual environment is up-to-date by running the following command: + +```python +python -m pip install --upgrade pip +``` + +## Install Python packages + +Install the Flask package by creating a _requirements.txt_ file in the _hello-python_ directory and adding the following line: + +```python +Flask==3.0.3 +``` + +Then, install the Flask package by running the following command: + +```python +python -m pip install -r requirements.txt +``` + +After Flask is installed, create a new file named _main.py_ in the _hello-python_ directory and add the following code: + +```python +import os +import flask + +app = flask.Flask(__name__) + +@app.route('/', methods=['GET']) +def hello_world(): + return 'Hello, World!' + +if __name__ == '__main__': + port = int(os.environ.get('PORT', 8111)) + app.run(host='0.0.0.0', port=port) +``` + +The preceding code creates a simple Flask app that listens on port 8111 and returns the message `"Hello, World!"` when the root endpoint is accessed. + +## Update the app host project + +Install the Python hosting package by running the following command: + +```dotnetcli +dotnet add ../PythonSample.AppHost/PythonSample.AppHost.csproj package Aspire.Hosting.Python --version 8.1.0 +``` + +After the package is installed, the project XML should have a new package reference similar to the following: + +```xml + + + + Exe + net8.0 + enable + enable + true + + + + + + + + + + +``` + +Update the app host _Program.cs_ file to include the Python project, by calling the `AddPythonProject` API and specifying the project name, project path, and the entry point file: + +:::code source="snippets/PythonSample/PythonSample.AppHost/Program.cs"::: + +## Run the app + +Now that you've added the Python hosting package, updated the app host _Program.cs_ file, and created a Python project, you can run the app host: + +```dotnetcli +dotnet run --project PythonSample.AppHost/PythonSample.AppHost.csproj +``` + +Launch the dashboard by clicking the link in the console output. The dashboard should display the Python project as a resource. + +:::image source="media/python-dashboard.png" lightbox="media/python-dashboard.png" alt-text=".NET Aspire dashboard: Python sample app."::: + +Select the **Endpoints** link to open the `hello-python` endpoint in a new browser tab. The browser should display the message "Hello, World!": + +:::image source="media/python-hello-world.png" lightbox="media/python-hello-world.png" alt-text=".NET Aspire dashboard: Python sample app endpoint."::: + +Stop the app host by pressing Ctrl + C in the terminal. + +## Add telemetry support. + +To add a bit of observability, add telemetry to help monitor the dependant Python app. In the Python project, add the following OpenTelemetry package as a dependency in the _requirements.txt_ file: + +:::code language="python" source="snippets/PythonSample/hello-python/requirements.txt" highlight="2"::: + +The preceding requirement update, adds the OpenTelemetry package and the OTLP exporter. Next, re-install the Python app requirements into the virtual environment by running the following command: + +```python +python -m pip install -r requirements.txt +``` + +The preceding command installs the OpenTelemetry package and the OTLP exporter, in the virtual environment. Update the Python app to include the OpenTelemetry code, by replacing the existing _main.py_ code with the following: + +:::code language="python" source="snippets/PythonSample/hello-python/main.py"::: + +Update the app host project's _launchSettings.json_ file to include the `ASPIRE_ALLOW_UNSECURED_TRANSPORT` environment variable: + +:::code language="json" source="snippets/PythonSample/PythonSample.AppHost/Properties/launchSettings.json"::: + +The `ASPIRE_ALLOW_UNSECURED_TRANSPORT` variable is required because when running locally the OpenTelemetry client in Python rejects the local development certificate. Launch the _app host_ again: + +```dotnetcli +dotnet run --project PythonSample.AppHost/PythonSample.AppHost.csproj +``` + +Once the app host has launched navigate to the dashboard and note that in addition to console log output, structured logging is also being routed through to the dashboard. + +:::image source="media/python-telemetry-in-dashboard.png" lightbox="media/python-telemetry-in-dashboard.png" alt-text=".NET Aspire dashboard: Structured logging from Python process."::: + +## Summary + +While there are several considerations that are beyond the scope of this article, you learned how to build .NET Aspire solution that integrates with Python. You also learned how to use the `AddPythonProject` API to host Python apps. diff --git a/docs/get-started/media/python-dashboard.png b/docs/get-started/media/python-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..5d68a57f249a3f172c5dbdff60077334d4f5a61a GIT binary patch literal 37570 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 bgFJ3>%4KV!2BdzvSda&jEfv@tJsNj$ zt<1OR`gqU*w~H4q_M~!3wUZ}e-RpuoPwMLF85yDj>-&sB>u(Q5Iw&c@bS6{Sd0aj} zFRU!}NX_WH%`0ii4<1io(4z?l_pix%L`3%}Smsy*eD012dB^h~M_T^+snlDuB*S>z|rxD8# z(!{|dU)D?*4}Izefj~y!l3`T7L?Sun3{>{mM{rO`gkn=K9EUY`Y&G2-{@MRP@AI;q z%Knljv`O8Xsg(I(a!*|a4>ChnX!eIg4_ zsoY_+eA+82Dms5in*grVD5*^Cy1zc@qT?){P#-`A=ms^wR6Z+taEZHQo}?xZk9d5c z!U4g+$dC7-{)_@M$Nb#lA@Ox>FU@zYQho%dr`O$%$(@a)xsPEV`jCcB&&wGAG301p z*N~P6rwvM0o%zI-{Ok18l%|b5`Nya89?nz}%-iC2um%t5q;4dnAWF+@2fWQ}$)49X ziC=AP(SCr=iW8DdvX|_CTs&m~niubFbvh`O1-p8$^c899gpk&8bi0#?iM2;1p3R%E zWPtr5y^a&!UT>EKv{x^vcbUntXtM=bgEzX7)h{{IiM6Z6|dx zRd-Cc#P|^t`f7E-%Imk0zVN7Lk$)ywQk#C~w|}QAzfq=CHibjy(%NxBufJrGl`NrKJ(c`x62W|LoY5GgtQ-@J@dc zR~W{K1`A+5g#{E971a4Q`2J-g`+EdpGWZU!udRGYM}Zd#%uwoA>%LyPDr^qNAwuB1 zFo-vwP~kR!o9-`03k%!r)Eq)R`P5hs{u+ge7FRrfz}M3)IVnhQwmW-EcSUw;B|4AW z){GhDIjN-^j<+8YjJP>`E5B6>#(O!@CfYkUlWwel3`)rEk+r zFwRhzR_q<8eH&vL;VD>Sf#eDVwkp89PPEOyIq3Bw5w(SPC$K?AGWUaj!g6ba z(z#?k42NR8gdXA}&i(`ajASZNdWuacah(^aG7$o(0p%I#t6oPDZFw?Nv z7}i6J+#j{K0}36}Ai}k9Cw$tnMLLF8R!(1$q~xw9pRI5)#KsiZYNI|rfDu77WVy4X zCK?9`cTc#Uy7uUCaUkX;GC3;v#MIx4(Cta}?`Hgxlu8afw_uQ+=l{484|vY!RFXL^ z9*NRE`r2t%=KYpuY0W$)26ONqszPD*AsPrp&^|#9x<)oEItgK0?j>)we|mzY$3?sL z1Vl;aGq@<;j)zrfk}Yl0<|>hh;b^msOn4%ux6grh!%{z)5vt$n%Kz)pPQ1=p`Oqj9jgqw+oE~+XN*@8z5)#rd=e(db-kx_`SMmjqI^QL z(;AQ}e?xnKkq_a+QTWqoYt#WMPy<(I>sMws)$#y5?3KT1clr#3{po&aG5RymYw2~~ zN>3#!-_7-hlZ;#63N*Y);Pr>9mj|a8SAUpk%=z0Ew5i9^VjxF#h{!`4R3Kj-R$2H7 zm6zwE1_fky?#9EDkSn8pC3Zm*PP`Y(?`YNPE{hYBzY*3n{9E106)o(lbW8(2twRz8 zT?t}n@cn^^&ShR{Y>H;VL#|}}6o0P~4#F&Z0efkb@A>vMRqVFxbbJ7Z6yu+^cjKuj)u zq|(B>RJJ(q$lJ<&y(5m^tAvnD`y7ZEotdeVt>5)5%xfXcuCG!v5C{9pD%zpROAJ-@ z3jaBNxapm$=5=d9Ch>@Wo!l!qN`~;-bDj7dqbhncHsOFUJGltj5BH1FP#W^)d(6p$ zMn;fhNTuKU5fXL}oNU`yiJl41Ej!ii)D-gh&K^pm&-r1#8u>|Wq;+tLiC<8gi61Og zaSr3!@PcGF>2hB{8ZtgX{(BA;<-%F@tP z;m7H{RA+|=^Y{R}P2eG6f!mZCegyv?hqCW+ZpS-Gf=MhOuUHS$-fmMQT+r^=^3d=8 zmdP5`8EZA3&^D|po>r0)%sMZrS)_#qdy;NM9;IpM%Hc5|l_i@eb#alOIioEve=hKD$kZ>L$ z03j|5Sbq)2vGEXSRN@~Xg0Vlrp9p!~wQo>z_w73A{>;>rerM~*AV^e@E5gZmcD>oV z&8Z;~D2925d~h~gHYDMKBz`Vq+RiPzT=1QsT^yMr@ZNGJUqv=_`jyo9BLhqg!TBgW zm=lp}osBlxmw%9m9P+b(J_Gnz1!9F3NgEL`NmRCF|1Ae0-;vWUdq2jKz8pl0-enlE zb0ggnv}~>SDT|j7iemc(2@(`zTz@@_!th&TpCir-#+LO1y;Z zlPLAXai;>=qf?id^Iu}vZ$za+7$*vTDilFP>19^xUHeTL@7-1VY4dqmxBHNJ5N?S| z>@h80(-$Pqjcze#p>(%f3}u384Y^KJ$ZT0q=2*88NK@y3&rz~5=eS=KOipADw6&Hll@B6p@-2K0YnEzu8x;@~38LV#$YFkhW8~oqD z?rbZ}w!&;H%=Vr2?_Phm@nIVuw((&bAGYy9VFfCO|816R8z=ui;ba5W*Q|8zORl=x z)V%P5cQW@wxk>i)Manl@dZ2QN(bMCq$WbIhN$a-&Clk0ADXM>UT!VIMjo!AM4@Rut z68b5M33fDW)dI}u0D++4&q^uXeYEG+zCS^rc?y-Y+@XD`}!pe@H^Y@%eoK zdvzg6iiQ7`q@DRter8F+Yap+!rHpj0G9s6zbFBLug&pygoG%O+N6BTIHph=2fOc&r zjdUuK+e>eZ3VKLz>Ga1}_rub@tngz)@?@%US=xNH4gipwm6i3Pwzf8hN~NZ(eR{xF zU$j2y7n#3f2wt|@T{QUg1GDB}|IGtxVnfWvX;joq3Zd?4BuMlpgfb=>bmijnA=* zA78D#4|CB})EL;<*k~v|VUlA?8jCgw3W?jNEdJtKJ4)|DB3D*cdNjS9G!$YH_;T|z zO@fkHiEgg0-UqzD6dfoIWGmrr)v@@mw)vV`uxNf17d76b%)N6TD02!Q*;~>LhKokm zMjH@XUF!o-@Xedw*NomC868zT_Z9t}rJ*4Q;vJwm4Hq2)w-`C^cF7fc`j=n-6M-od AI{*Lx literal 0 HcmV?d00001 diff --git a/docs/get-started/media/python-telemetry-in-dashboard.png b/docs/get-started/media/python-telemetry-in-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..44f4d4940b3a4272db599026e28e9c9712645ae9 GIT binary patch literal 47387 zcmcGV1yCGa*XMB(G`PD5$x>2D5)Q694*kIx8Fu{K>5Yyn92{2n(+_;VW04sgTw~Q+ z8A%NRoK+QIHNujX~cYj)`&ZIP_ z?yF7n)MAY+8S6^`TRnei9pm-T`CUoGk&+MCX)=;pE7;_Dfq-5kqZ8XIRn?oi0)_+P|M60}PjcziRj)E)OC7@Yicj(4T*8 zCFeBut6Wm#LBKqpB(Xl#=~z*xIEFuc&WBZ(Hkj^XC!mm2PMctIX`GzgiQv14r*q5# zm^*E)564gE`qIc{{(cgOLSGJ4?^IH)UIEGboseTfb4jscC`VNOoNlrfJlWdvjJrM= z{~6TY-cEzbGw{7HkpIeblBa*_cIx-HRp>E`$<7q>Kkqg15rcJrW^w!nnfE;O0el8)3bFwOlzgso4Cvl)*DdoCmcE^ z<=6hzx7pE}58%AQb!jFZE6KmlMmHY#coVoGFL96KakP+M*mJ#x9Y`#&?VWj1H!S?K_t=k?Z z@lQ9PKqF*>0+sAoc}j1YaW215-hs2r?y$3mli1>p(6UZrsP9A1bH8f^Q)lxlL7`7i zLl+)y)pI*t84vR$7NUtxO-+5+`<=gkq4=#%v!ZbwZOP}L+~%QQ0?HL| z(*jz%0Y0{oQYlN%ob9HAV`)z>(*=DF4amW(&sH8+iDRk4h-DaY9wEh7%q!mNiqyu~ zX+-0Fhs{bp87N_UZ~ych;>N|D(@EP+0l{i~mhZ6;fmOVcBFaVtQhF}AwPC!ml_L}-T0x7n{1Lyty{nT~q z#h6HnfMZq`V&mplg(=>qaa1gFRcprNyA7(xq8(ykX|-<~w`Uu3rx)U_Ehl#P(cV;! ztyiooyFz59oLI=nRKHybzI*i%`)yRHU)?^dEyk)%ZaBc1)Xwmqpj~q6pLN=rdhVmk zI3RT=RhHR~rKc6fGgc+^lLA_IlXEwpTm?1|Qxbmk5Zx>7!50&DiK)8$vs!aAsKG0Z zTS-_M*}-*p`EEL;hyH*D_ny7px%o zQGHGK2LJuIg21mc(yWR*c-;)!(+~N48k|9BTP_<3K3shssHgi` zVqVTC`PwIXUDT+5?7W#adY^{U4MG!Q!T(4oBC;JZLRpTmxnEa1q zqX3;z9?LOu&B@LE(#pOaiAV40H?x!q>GL)a$X1dZA1)~jmB3TEjEg576 zjV0fsFq|GBCxKd{CJrq}{CoqzEnX7UCg}aaw5SUS3B8T2ZMW~qpg6}h$JU-59#7(Hi8kJ4_t+fGjr z@vOKn%gvQS*1kSoE|uULFt2Dme|L5BI*7bxFbd<>K*NxBF#ma7bxmmB{nf!Vo81=m zW76b^*3didDw^a(0xFbq2x8(ib-$h?WmjpSuqOQo7rBUshCBcLr#C+~`awjGl-*5K zY`tV*Swz`wg(d3)%5Ti!e(W)YIsXjYoY}4-D?LAM@w%~+Sa3*>nXh&1nXE>jY&q`M zthMYu0>vr0yR>h`xA`3!wx~Y5>?g|ej^nZ&$T_lVKVtQJ?=Mc4ZFKK1N@lwZ@jX2h z6EHd3iMg9&*1p@)dD?z-jP4 z9kieaJwk7$L6vRSX*Tt=h-@A;BvHKa)WLot(RR3W!4!3Mbv~@8bEa{{+uWWYe*s-R zz3Hd^-n1+@@!JvE_ch0(n(r|$GUFRK1IIY7pPW#1bOIkSPbj_d!a%;L^0DnvbdW&# z$LelDmqxf9e;eG`)<;6vHt^Z>h^DVY7xvZVew~ul&DAhDoQNLPF51*Uy$yRC8vpy7inR=3@J`{GKbMoAUmMXgcn% z_S^GMvJg;rK011p(R_pBw{^a_8u-$AEpYN?i=VvXvJ#?d-;G@PbVpBWasQEqa@mCvw`m+>{RSz25))lLV*-kk;d&OB=(vv02C<#0V zF2OVoZAe~w&Rl_+7=iR2R_YWLCGBM6apfIH+@n1n`U{}gmeNY8j7qwv#gYq{A#arO@?ffe`X@s%lw{;3%GWynI!p2uBG z92r4F2NoZ$A5ZA z32VW}^|;m5c4sett;U;~FGYcmwx^(Lf+Yy6)5MX$Yxg#nGhB8dJ^Y^nD0X$v4P_LD z0nr9Ok)u1;W)`P&!r5y=^b9 zZ+bOXf;_l-uY$lzh?Am0{7mGw-GF4D$LuOp;zQ{NXx9A;?`o@BHzzcj?(w zb1A@&`eO^wg+#pN66_^xShW#kJ8>I^soh53Iu=?wj-T2Y$M2i5b*IH;zKpV@!*bJt z>nUE3RZ%TJ0S!JextaCzQgzhY%8Y4*_>tgQOI}*f#~RrI;em@)x>p1)d->>9oBb+l zvnCQN_C}w;xS9Qsbf?)!4|4bNPW5)jJhg(}PKxI@J$ zGmo}-ta;te{ngG(%FXm)rhA8Z>@bYJsoB}Ed(>)fM4n*(m~w-%k8y_|rg`I)m3bK0 zB(YLIn#N{DsZ00?qdDP<14Qw7I3AE<$%Ue^%^u0tKMZcrrx*Gat>XKwQ!$pd3&A$8 zPh18RJqV)Up0Hf*J||nFigelVHCLm1v|}u|vfg~(4@dM~Pyz6#t` zpY6_T*T}S|G067LuG`j+aauGIt}2rE((sJ^qUzW<;`VZ3OOippW&E%*Y9!5N*xTo! zH^n*AoSFQgO#cR6OfsGGWO1(Q3fro6Ii$8l?=-klKXlm9HVO@4M9+oiRn^t9b0-5) z#N&xvgP4_0>obCx4vV$kMh%8hhDMJs?(&RlCWUXt^IEFdSofV6lk|Oy=Z4?#RJ6kr zML@jHdT$LnuTd%G!4ulUD!usx>tI74%4Hw4KMqHXI3D9?1vCrH5Vy?vF!eFtP%H@I_X!2H`3+(epa4myP3)=RLR7fYt!1 zl0r|_@$BtUc0gF@P;u4!j+AxXpo=rsH0e(v0YB~Sh>*by;mklL9{&<%mqkTEyu!%9 zX3qonsA&4f=3A5bQ-)BM-uOZ}&xWHP_)LmZ&Epv*Hc4(Lb@KE-o;QAX;p2bUYtRln zx)XeH`>Ne7udQBM!lP}s-yifSZj)pfJIwLe>T~3v2ml#{t8c|dw24for36jv48pgP zT@j{5IR%uty8lj%FAwmLmwn%bMIN6`(LrbQ9JPF8&FDB;jM=kW(d5tGPU>IgZ+y7e zfW266KWREhA><;{mf1UWNJXb<5x)@#-3gomRu43kK7fIbEtFCGDb)Sjqv;dBetlm_ zIM1Vkyiq|;dSy^@Pu>Iz+W>}!u^a(sQAMSt-TmU%Tvw@YXI_079-mLrDAo`RB)%okUdInP+>ViZB!s~KMEZD<-1ysXYhN33@=Q(e zIT7fv8xIs;M{(T8F~UewV-!Cu5f-RZPiXIZ@4PKLyFRmc($nmypKCb~E5pJ&gX3xfi=KY#u#0*<=po6eN!+@6L>l-Wc^Q%M-A@ZBTcjXDMdlR38H>|}ao zrKvz6w;OS#InM?TDqzah0AVUOR z{vNxup>%*c6eDzCTqXPTIl0iCCb|Ek7#H-D?t9@GqmMfQ$)a>I?9GV3+w868kq;fM z`!1ThC!$3z6bS^q&wA%b^vGXKAFk&{{T`o+P;C2&V5yfjdT6Z^m4w)P+lM#4R*b5( zG3Ln88Kl6`+ge=WBbU@uY=5?(5bxM7_UNa>!BAgaIP|O!9-mMz=siwwc_9AZNu;@1 zs?#4$63jCuh}f{?66KRw;@3UYcC%6{yzY0i?0J=ea>t+a6f2d~j)5S@o<1dO*^hVG zS4PF){=f(MX_-yOKrq=c@G(G)?<+=Yila{Tw?!84L40L9WNAmdu&_Y6J?MRV?)6hZ zT3MLr=={Y|ElIxvjSJ|gvSYL$8bxR&08EW` zKmPs`Cc%&R#}Ft1PxzjB_C33gVW44~>=t5eUud*tc+O8VeyAsi5f(HTrCBL*bd&~) zVuEkl^0r4wh&W#N&b3)tTTA<1l;&W`o6g%izWW+0#l)J?F%e^n1Hny{o^u0@SCbLt ztbhH4q|Lv!*1~Tb7#+#ml4aj%m=VL$Y%^5hFyCV;Oe+EvGvl2cOk1{cSaE3e;Fs~+ zfyAx8{_3CQHi(eHv27EuIse1a`0oUqkmnzam&7Dtj)$lWeD8#z3Jq%0tNrN`RmUr% z$15Y7Ea1Zyhfky=zi-pS`S6wYzS)P@0po*A*=06wdu(30!X}%^bcv=9ds%HQ9vQz~ z$6U4P87%bn>W~cG-?yoYh2fj`Z46t55|CmvtAW+XI%$o&hS>wn>_P9%Ru_`HHirhi zaXwyd(oMlC%d$D4JPAyapT8b=Nh$hbiDDs~;#@~|)9U4if!z;^P!Wg2RKcC42(L%~ zJUlf5a=>$~j}qeEivIG)jl>E`88L{^meY&nGhy<~Nc7FpXuacp1N-sy`EBs2+XCb@ zzDZ~1!K!c_neF42OXJB|X}!^SJzt)~nR&c{N#hKJI41xwIo0MY)PC7CZfZSxC6F)-8NW{N$eC6(eY&WrH?-S!D+ z#IKJBLTmTQq*1uqAxDm-v-JxgYTEQ4a|DEhB?WBUs+=!sI52ghWOVE8Nc_aJnMKGF zk?D07r9Doqzi#bZUtj;?9!} zMwQ(>Gzoon`Y)}8GO2*sc;7aLEUM!mr&H@2F=g&NUqh*O}N6~f!Al9dhO*WPg&OBsp=H3rh^^|N+P(mvmu9;XH)7b z#iS%Pdr{=cpU~&c`sqao6DMCaZQ}C%u_ZecHpu?|{&Jsb5TQHMz`RvR?f0~TXWZXm zLm;eeER%tD`kM_t!|O&rLl~+UP2yUU!Dr^{Np^a3!FjwHw<^s31y8G zs#rU2=Qw+0 zUm)b;0sy7mMW2xH&&>kq6&cE)>~U(Be%(hp2KtnFRbf zJEYX@&EZIRE`M=jdEX*$uY^VThUHsI_Mh;;WzuP7c=&|!2$XGo_)VHKPfKR+L;D{a zKExsI9GAT-A_r?0A(*_&Nv?v~lk30D3H-SaE$tBce`XQlga6zCmL-6F`)@DHBoGmA z{zXuF-V?_<105S>i{CJi3g~j1=iva`+u+tyEwegDQhA&Q8InJYq)f5KkV^!R zkBi-qdmJwn&j0{`9Em~p|LU4^g_$67Eq!O?F_9C|GJ}@)DsRV?1eQMwfc)@)FfR>u zE0cdaof&9Wt`h4HzBYy-z5NrInO;{gy2rH`&ngdf^gqz^g5AC<@G*ehs9luT07Wh< z;NH4!q)eyw1qRAA=%3M&88oZgmem+F%%{x=5X;(po?|BV=d zlJQMZ7vX=eZw@^qsHu?#900%S=+Vts{`(wz#9v`9Yx+hq@FBmXm7$x-DeE4i^Y%bO4 zMkSGK^c$^BH*M{i6w^Mx-R6{3iI$v!KI5J4HsC@{Y+s7`xH%l~H+ctul6<9>_w;P= zaX*BGnAPUPgnUO^$wp7Pt^#NhJoa5`hP*P>7yemO9oxq3J|rzM1QDMqrNg)-CO*4^ z&e&0vyZ`_TTFkr@e`Pc7G#izS{A>XE)T9fb{qQei$JxrJv8~{?h(6QY2L6t~W10e5 zTo4>s(kWR+QORF{Qrsx~4%h%@ac0j#zFFqFC?S?aULyUof=F?taWw_E64W)6Fz4B2 zJ)~G;U_l1v*9=C9Zx<<(a%sV+(ll5fOmG+vB>p53VVNki0h{>lvf?vX6oS{*)>Z<- zyi1#dZIQJ_MMWJ%Kv=Z9&65p)5klBjcYECcEKlM}reI+tQu-bEyp!=KEGoxC`z{Cc z&TrOK!3{FByp3UhMKzN3GS^6GAug-HJjKXHvDEIjD~gTDD=L7n z;`|mN+Z>X68EB0Miw?I6uEF1MiYmg z#g(=T{*a9#Tx$SA%1erA!eH6TLcyb-lbqh?W=~1DoV{_;3yg~9eb%~>>!U?3_kE44 zn>U=bHYDWtj)2zwG~`!hRtKXd@Ksb|?eEyN=VQeUA~rvS0uMhFVr$k8IhmJF^oL#< zaLM0z=4^EG(QUls0?SDH2k=oIb2n6hz8=d7kGj3@UR(%)B%ukoQ0Z#DlAF&Uc-9l` zcG+RXhdP$N3DvuXs=m;pY=7>`<~R3c6c^g5eA{y~-d011?GbJtCkpq>VLm)jVaG%{ z(a6r)4i%A&v(S#~z1$%faCov1zJPoIz?-Wq?6qL0I=PPHiUt(H8sOE*KfWOdB9NSg{A9{2jzarhkUP6T?52-yrf_UAZMdYsR z1X?I{R3ueforzVKcchZYL9L6IbMzz4%8rvI2b{x8VGuGv+lV7u*$rJ)E^gfnQVK8X z)(qt2e7%70i{s>A2IXxDVYlrawA%Ui{Y_l04e#bfhWb#@bJ}tY&>e_yi~=y^TsUNHs7|pYXc~j%<-;1zo}t~oS*7md;Wcy zVwtDdow8d<`|>093NphNN82x>nq6$t-j!&=^isiYo}7N+Z*~>y zmtCZWDV+VmR|h4Wo;BuVblW1R3Y4~=JS!L=*ra@~qu~a%&%Bdw_5Bepr%a(isK$(v zdy)LN$vTrdhmZHL8EX2RcfNrqB7DPIkKg1G5J@J$n=_N-d0odIZ0p@pVQjh>Q0@P6 z*h0$BE=4sKRhYD@qd79z-sI7-1}U8OLb9?_$}V^Xs8d=sZ$v;)v@hZHB1MpE-~Qh$ zJm5We^Y%O$7!BJoL(}qp2#cQe;OSsGVd&GmEAgBA-2|U?S6{dU7FjjW553D>&2GdF z*NBn5Lwu=iitS|fI#5Acu;Y_Zk)w_yy3-pMBRWL#?mpOJX7q&lkh}UuRA`^04b-Se zR^u6k0WC{*W^VYnkzZdSKJwZ_mMYLf%w(EX5Ao?3W!hO8Or|uM}jI-*vXHq>QSek^If{<&tyL zJ8BJz;0>={+r!LzoUx{NV?WfSRzDc{RCv-AQ(t487;@MwEXo>|#MeX|7PhG+7Ix*|h8LK8|c zmpmHq=I@lQ373qOWX=#J(!@Nb9*dC zOPG>{JVk=-y+IJAo9;rbT^TaRL3n*ulX*mrFEne+Nu9i(Jc02( zHr0IXbr00ZK5FH=`HtmSG!7CsgLc!QWkEpu8+iLieo=j2hV3!-&WsXGs{`|&f4x^1 z^k8xlRqdZ}whP&PF+01@rF_+g2mIMW@(+cL?fBoFHJQ;eS{8OGCh1_We})%eVJW`M zk)D3@l_D|p2z1Wr;4&tY&ynxR@Fzuv6Y~ti#Vnfn#e1C+*FzDjm!Vi~P-&dX&zR}a zgGmAW^>*ldABojaW$!VRaV$c=`XVIpI`nOizC{U4JpS%Z%NY9ZQqqm_O_0Bsj#rq& z#E8>T^eUkn!w|DbS)I5=sBp^53@hvP-_ry)1hsPw63yTA$@1jX(3^Wa>-bil*?RIU zwmMAljQySMLP3(T82ih{)>84BNjuk_Sawl>H^+{Sl3`F0H5!XiY*f>0a9WklVpB6L$mC6d6YaZI}W z?nqU_J!#+8#DBVr6T{^Pa}_I~FdDLZ#nJ7eaD#2SGZJ~Gt0jH%!4`ESBTDFAMdyv7 zrK6e?=SPApr?t6?A`>02yS!2D_tT%ViXdMr;|-7lgOp<0sAN<4*p^O>fsSGecILN~ zAeZ4q0~7}bbMK<>K>K{Mt4!x0WK@wgy{*~I*mvK!fV2+_33=ti)m`mh1jFmo%&o?x zq7j|C+RTEWdgPs3n%Y$Ua86bBAp`2$C>e+ii@2#QrLXhz`6E5&?~6?5K(&;|iHn!o zymvlxbDf!kICcJVzQK%tT%jR~r^uY?5_EQC-M1cMTdfyI2)d`|XHU)TcbWWzh{|cd zz$=!d|GfYyN1UBWvFk_IV2V&6_qF@s>k}xXzJTVUx?(a6O~D9TWPfLM2(`P8i2Q=c zc!vH%U?9nRKH^bk+V;Fw95GB~F_Q*tXh~C1ooH@0Zz=hOIcgfjf7fa`2(ptA?0SEH zX{$zYinOp+kFW3RN1(l0clkQ9nmNpC*lK#7xQ&-6gxXc&OVP zY5Bs9OQ7kX*or)ubmZ;s!Q?^piESnJt3oY-$?%oXs$Dl?Jz*-%#KaWWG3_(+#b3YI zS`{DssM^rpPFGiGmTexe7_i zUV_A&irod#~cKVK(~M zz0^o$iOg1pt6PVaM7?Mu7Pcc#oOA9a)1Qtf)lsV$6TX2@I{y$PQazX^AHrPP+kAeSX08;BRc9y! z0w}&fhPodsUS6~O?wm!5GnbF^a9=e(aanSkRBE)|3YL>JLgUlqO6)v{Q>1GubAMD2 z3n+`cflha_x|t~Tn`)cvm0hduf@9pcN1L4y)9m1}tX;Nzcwcb`(S}0ie%r8DUi<#0 z<>fCA7c>OKM*2Ne5e=LB6dUI=bZ|Yda98>VF961fwOtd}(K$7tY_?t_Suyi2MU%!b zy+L0iao4v?(WJX;UJfk(ikUol{^^K4lN7NQWn{HYKb!-FGZCKArUJ~k@@AZ%I|!x(2yFeu8q(Th>8$HnGj_kDaeht8NkjRt57rvK>rd79V-L` zE|Y&2aAjvqOfsDgRn~MhdEL*biMP|WSdD1URquaDkGjo%{++7|5xT(rCAT`<&2Td3 zX1V4Psrt|zoBCHZ%9xZb>d!B}Jl;^3rK0oNtvPhO9_O?`Q?ab$rJySU5)rk(A)$`I z$8FYEWsA+E7geaEQ76)8D0dAR`B?BfHu#_h;ugo1kE*tWBpis8tgP zoES!fBL!Y7B^>}OiOG7xAN-Dxn}wq7b+}AzK&e4)>7_5Weo$PcxNZe=?pv@+nU0hG zSXmAIvt=?j{8-jE&n%^QSyJEg$_}&~yVZ=++f{co*N&djaR1)Uh`HdULmwsAY}WF! zlV*w}+L?C<`f|$_(;0r=Z)+L0!{LJaOd0wsx~i2zMDnhTCfFwXR}nfSZ!Mse>UmMV zYseP-T-{l+x@?*TyvK=>*VQXpmb8KST4|Y4ktHkEgN`-V&c?d`(u6J?2hHPffpULp zYN(xE?Lj#gKvRY00NNIGd=LsByJyu1TNy)WEMeuIRs6f-TOqw`YIhw3lIv?yy8ahT32-*m;PhaCP&W%GiA;P43}0BA8+{b`6$slQuS{3AD1FT6ZJ|IDA76 z^roZ|a-ruyk8PFPBD#RG8C|f+atC8}O9lv}m(o+y^~2PtT1>PYWioe?S;OZUNjvqT z{Sk6(&_0|k{)^&941~>%6Ou~6T`%l$1RTgufO4PK%KRCC>5{QXW(ZtEZxXUMn@0C5 zdDG4WVwoj25B^;Jc4lY|LS(M@KCSd{z83Xn$rZcaVY#H_Gxq1`cjH-NWFY8u1DE*_ zz8IgYnso(CyLGY-)BJ{0(&hSH!06HjjLJ`&%H*L3ZiJu9i$k-{NE3lxkFxVgiPZg= z0!tsC5?@athUn_EGP|%F7|}=Ju^8!qWoTSsO+N8a=)iBQK9FGULnmy0;;sY&>dhGj z&YH&ku@Vy#Z)Y0X^9wOX()esoVZFExBXi%fz6w2&*FZCC-VIpqL1EL&<@pJad9mg< zQ`q72J6Qg=iju5xQdvZ){9lU7oJzCjbqP}@zK7UqqYB0RU6bL=>Y?-lYaT6{5)?C% z$}nY6?$Y$~a?HYlZbEW$srXOv$Gh>;Gc6;3&GDzX&-)X0`58U+{g!~uNZru5mR5hTZ&=9uLf7tPywvNNQ@(zs8-d!$W;3DtSQ-B7JS zUgItI!@0W{a?xaug<0=i=ndqH4XxMDXD~K;UC`zWY@wU;A!37{q?uSZ>(Q!NC^m zJx<}w@hZ9PFxkZ6&hqfi+6&3%-IQaLd(jsX?JaX6KVjFadwpgA)#kH0h)JM{oh#!t zLxO{e*#60{dxWCuAB#@l2_V7xHS&fU=`r+Ojv;Fk&2txh-=jLfsOF(`0Iv}a(Qm55 zjzlelxH?(cC+l!7rLK34% zpiippycHZ>@u?Zifobf_H6ZoHL@36%%%?SUu5?(J*wl8GhwK;5ty)P}aI~O8O zCHl8XvraY`J5CTf0X~Ya_+2O|W(sSnL>%E|-+s24>2m$b>{(=Uo?`)jzvocK)K+iJ zm0VlP$1#vGJ8`XzU7=472$S|AB+F{Ybve(Es+1WwwJ#BR$2w+Eh0kC;1trFmTY+e0 zEReEhYvvJ1dS%i|=%y+3jutlSB&rCUaq4;uT%}(mb>st?YxeqAg_gvh0zVO9SAVn``w@j>>uVUkU)gkajG4M$103uJlRVpgW`BXNP!R76eq zF>DSdur_Hk=p{ejdC7K;z-CI${dwdWC!oIdc0KBecv761`oM}k_>N#Ega>AJFEwa~ zLe-UpkS7aO`i&m%wZs_e4O%?b{um~mnj;zoR|0e)NdNfyOSinY{EprOW#eZkSxv*g z4;gq31pK{sNgzV$ZNGNu&{@X{So0!Vh!L`0X$(uLd_mWYeSiCsA^CM1(dm@b`;T!g zOYwV>mMYJO-dz^$&0~7q5B17jBq|VX3}yAlyV#z)DsbY7{OX?Bp%HR@%ulQ4%_Yb3 zLZDTtZ_$hi{c*>;D{Gm5mTEVTQ&cRcRg|g-M;VsIu@Sj?UA#~3j6lTQvG%#)C%`d^ zm{eiSy$_3VpnwIwsd}sL;Y_!fc?{(eNt8B!Xo7jR)FKH!)+YS|m0x z4ZB5ha4fEFU8sL2rtX7#HTp%_h`hkqvEr+Mqs|w5Xcazd9}OphVgiNdvU~FV4H@V* z)F+p{bZpo*;u2;vRUry8^9)8L;3vh04?F+7hHSf zjx*ENhvG%I+T=y2y>>?Jb@JCY!yQ^%mY%DTDfS=2&owy~(u0_9h>@j=2aY7YOO5co zBUejYzVqR4J?jANzfM#jpul6d{8oh>(wP?VHN1eeCR=n~qql6?nT613{+nF3H@6jS zFq2P0=oOT0*Bz^-;5FLa)S6`ZOX?RG<{xPVGyA?i2R9HiJ3AB!%KJx)9oWmOSCPle zoVd+&nWBqYD0alng#UEPeQ&3y-oh{hIIhm@H+YstC6dW{n_3~`@A_ksD2#uw%{y7XoX+bqSA=0O zMaENEjFRa~tlS*uNhN_NB>mFka4x4%=4<6Q2u#Kn152-nnOC{RV2Ca8#oG`%J_r~F z*b2AfM;c~?u(k72EI-;uxF|m229xo=i$G{AS#uzy-=Z#kAPY$jN62AE3PBT!NG9Wl zbqqk<|6bSM=vAogbh$8igsf<-^)d3$>!&eX$nNDuF@^hWJEkd24Hy zrq#`vi3csXh9%jLo&4!~wJuGla4 zsdnhYPy)-oBZ^>nXsjRH*#KSKhvjB>uhULc91N5R5UA4O_0}!ltbQ<;Y2Wt-0d;{7 zUnt^MxZ>aRWQnb9vu*#H!aQ8WTV4vWnqrYmmDW1JCSct9Fw^RkTeIZlLVnti+xnZ` zf>F<*NQVFA7s%$7{@t5R|GQ(@7GQmqN|g6}ggLq8#t3n^uw*7Qg+p~3$#MpfLYbF0 zDX*>><;W*eO$|l93a?rUBR_T|+f0_*CpW!W1|!0zvPf6grCsj3j~SA14esVG^adFB zR1@`7?eVANVNC#dhbexMI=+f3ar?>e;29$f{C-X^`TS!M8W*m{7^q=xyz^AUgh(#a zy$Vy=6`@%vW{Hw$YHF5bW05S)wfnc&4AD|)!&2*+nVCKj#*7XFDiRR2C5z`tMKmjm zI^l8z^Yjd7dXyQ7xp3Q+pMZ7bImGg4!y#tn8vx(QuU-^i3MMSGN}iiIB1o$_tOVwN zQAsqswduMj2?8eV#mdtOlKs+NL909zocaOoiP)8(R}J0|zu*kcNUXL^=%K4;5Us>u z+><~ZSPCHu2ztSk;Nhy*@N6pFt@wR9oj3K{1Tso_6I>?(^oAF(RcV)0X{LEGG6tsEQ ztUog!L{yXAO1V+a>H%B(60d*HY>H*>a*4dmCpWvKGxl$J=X3Y-8J6~TBE)GQOR;uz zo?1kyjy0%73{EB4LD@2k-2QuoK)XtWL zZ?(bO#BX3G@hO=AxN~SgM8*2B@YC9dy&finAfmb(kiR6x*-D3{xJAH@fz+fqq5A^L z*W)ycj*l=0mRuW8gT(IkjMA?d>N9XGO*ha`^x%Q!pkBvSzm~HAG8wX8Q5m+sSjuVjOr|u%~$yPc;ZrW_pb$U-|{zuOy$b)U`Vm=C7Bs2i9Uei@~_N^-Wsg z+t*pre?Uxt>|x*Tf44PijWsg77IF)46gW0bpksS-sr#tq5nzt*mcWz8%%1>su57<& zeP5v;@p$tH&CZgNN$sHyJ3Sm_ZYT6IyYUY$`E+(T+S^DN_d$S;T&Ek%F~pA|gbVYQ znSp2WNpd_-p#wIG5OeY=G^*w$oBuy54ICUyzW>U?|1ScC|BS(c(OeF#4Ws94Ee~?A z(l0Ng7bl~8-F5VQrI?hXhXQmW&MC+3X|qsecxzxGL7Q1{(z^pu0Cfo8COmY|fq#y`<1cP*lX@~I6^^%}M zUM6D=J~ljrAgyRj8?l=lui$E^*r@w6L9MW4mF|D(C}FlJ$yQ6o*5oV04-qcet*3)z zOn1i+SjQa{%j8W~7_|JFpD#wkt*MfcX_7|a9~0cbNJvaH-k&P=u?bl7_kylMW_O>; zV1ue%;sY(e-BE0NVhIiyBT?A#3FQ_zN-eK1H+l!-eF|UgFa zB)|?YH5rp{{>$|2IK8@)oMj7QGJ`-z8vZ-^ejT

daP`kzXi|D5Rm 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 BRxSVl literal 0 HcmV?d00001 diff --git a/docs/get-started/snippets/PythonSample/PythonSample.AppHost/Program.cs b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/Program.cs new file mode 100644 index 0000000000..15b370cffe --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/Program.cs @@ -0,0 +1,8 @@ +var builder = DistributedApplication.CreateBuilder(args); + +builder.AddPythonProject("hello-python", "../hello-python", "main.py") + .WithEndpoint(targetPort: 8111, scheme: "http", env: "PORT") + //.WithEnvironment("OTEL_PYTHON_OTLP_TRACES_SSL", "false") + ; + +builder.Build().Run(); diff --git a/docs/get-started/snippets/PythonSample/PythonSample.AppHost/Properties/launchSettings.json b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..868c4ed01b --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/Properties/launchSettings.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15209", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19171", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20208", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + } + } + } +} diff --git a/docs/get-started/snippets/PythonSample/PythonSample.AppHost/PythonSample.AppHost.csproj b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/PythonSample.AppHost.csproj new file mode 100644 index 0000000000..e3c32cb096 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/PythonSample.AppHost.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + enable + enable + true + 5fd92a87-fff8-4a09-9f6e-2c0d656e25ba + + + + + + + + diff --git a/docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.Development.json b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.json b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/Extensions.cs b/docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..ce94dc2c43 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/Extensions.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +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 +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.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 IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // 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("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/PythonSample.ServiceDefaults.csproj b/docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/PythonSample.ServiceDefaults.csproj new file mode 100644 index 0000000000..589e39190a --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.ServiceDefaults/PythonSample.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/get-started/snippets/PythonSample/PythonSample.sln b/docs/get-started/snippets/PythonSample/PythonSample.sln new file mode 100644 index 0000000000..a80fd19c03 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/PythonSample.sln @@ -0,0 +1,30 @@ +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}") = "PythonSample.AppHost", "PythonSample.AppHost\PythonSample.AppHost.csproj", "{A80C6371-75DE-47A1-8738-385C54E4B590}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PythonSample.ServiceDefaults", "PythonSample.ServiceDefaults\PythonSample.ServiceDefaults.csproj", "{687DF80B-3F67-49D7-8A94-B67EA5BD9150}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A80C6371-75DE-47A1-8738-385C54E4B590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A80C6371-75DE-47A1-8738-385C54E4B590}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A80C6371-75DE-47A1-8738-385C54E4B590}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A80C6371-75DE-47A1-8738-385C54E4B590}.Release|Any CPU.Build.0 = Release|Any CPU + {687DF80B-3F67-49D7-8A94-B67EA5BD9150}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {687DF80B-3F67-49D7-8A94-B67EA5BD9150}.Debug|Any CPU.Build.0 = Debug|Any CPU + {687DF80B-3F67-49D7-8A94-B67EA5BD9150}.Release|Any CPU.ActiveCfg = Release|Any CPU + {687DF80B-3F67-49D7-8A94-B67EA5BD9150}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {31663D15-365E-4029-A40F-C4095F36802A} + EndGlobalSection +EndGlobal diff --git a/docs/get-started/snippets/PythonSample/hello-python/main.py b/docs/get-started/snippets/PythonSample/hello-python/main.py new file mode 100644 index 0000000000..7f1da16246 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/hello-python/main.py @@ -0,0 +1,17 @@ +import os +import flask +import logging + +logging.basicConfig() +logging.getLogger().setLevel(logging.NOTSET) + +app = flask.Flask(__name__) + +@app.route('/', methods=['GET']) +def hello_world(): + logging.getLogger(__name__).info("request received!") + return 'Hello, World!' + +if __name__ == '__main__': + port = int(os.environ.get('PORT', 8111)) + app.run(host='0.0.0.0', port=port) \ No newline at end of file diff --git a/docs/get-started/snippets/PythonSample/hello-python/requirements.txt b/docs/get-started/snippets/PythonSample/hello-python/requirements.txt new file mode 100644 index 0000000000..a626628ba2 --- /dev/null +++ b/docs/get-started/snippets/PythonSample/hello-python/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.3 +opentelemetry-distro[otlp] \ No newline at end of file diff --git a/docs/get-started/snippets/quickstart/AspireSample/AspireSample.AppHost/AspireSample.AppHost.csproj b/docs/get-started/snippets/quickstart/AspireSample/AspireSample.AppHost/AspireSample.AppHost.csproj index c7ccd1f190..a45d57e615 100644 --- a/docs/get-started/snippets/quickstart/AspireSample/AspireSample.AppHost/AspireSample.AppHost.csproj +++ b/docs/get-started/snippets/quickstart/AspireSample/AspireSample.AppHost/AspireSample.AppHost.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/docs/get-started/snippets/quickstart/AspireSample/AspireSample.ServiceDefaults/AspireSample.ServiceDefaults.csproj b/docs/get-started/snippets/quickstart/AspireSample/AspireSample.ServiceDefaults/AspireSample.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/get-started/snippets/quickstart/AspireSample/AspireSample.ServiceDefaults/AspireSample.ServiceDefaults.csproj +++ b/docs/get-started/snippets/quickstart/AspireSample/AspireSample.ServiceDefaults/AspireSample.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/get-started/snippets/quickstart/AspireSample/AspireSample.Web/AspireSample.Web.csproj b/docs/get-started/snippets/quickstart/AspireSample/AspireSample.Web/AspireSample.Web.csproj index 95909a7892..cee24fbc67 100644 --- a/docs/get-started/snippets/quickstart/AspireSample/AspireSample.Web/AspireSample.Web.csproj +++ b/docs/get-started/snippets/quickstart/AspireSample/AspireSample.Web/AspireSample.Web.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/index.yml b/docs/index.yml index 1121bf09a2..bdf17a369c 100644 --- a/docs/index.yml +++ b/docs/index.yml @@ -136,6 +136,9 @@ conceptualContent: - itemType: overview text: Azure Service Bus url: messaging/azure-service-bus-component.md + - itemType: overview + text: Azure Web PubSub + url: messaging/azure-web-pubsub-component.md - itemType: how-to-guide text: RabbitMQ client .NET Aspire component url: messaging/rabbitmq-client-component.md @@ -235,6 +238,15 @@ conceptualContent: - itemType: training text: Use telemetry in a .NET Aspire project url: /training/modules/use-telemetry-dotnet-aspire + - itemType: training + text: Use databases in a .NET Aspire project + url: /training/modules/use-databases-dotnet-aspire-app/ + - itemType: training + text: Improve performance with a cache in a .NET Aspire project + url: /training/modules/improve-performance-cache-aspire/ + - itemType: training + text: Send messages with RabbitMQ in a .NET Aspire project + url: /training/modules/send-messages-rabbitmq-dotnet-aspire-app/ additionalContent: sections: diff --git a/docs/messaging/azure-event-hubs-component.md b/docs/messaging/azure-event-hubs-component.md index d9edff769e..9bf03865ed 100644 --- a/docs/messaging/azure-event-hubs-component.md +++ b/docs/messaging/azure-event-hubs-component.md @@ -2,7 +2,7 @@ title: .NET Aspire Azure Event Hubs component description: This article describes the .NET Aspire Azure Event Hubs component features and capabilities. ms.topic: how-to -ms.date: 07/17/2024 +ms.date: 07/23/2024 --- # .NET Aspire Azure Event Hubs component @@ -94,12 +94,22 @@ var builder = DistributedApplication.CreateBuilder(args); var eventHubs = builder.AddAzureEventHubs("eventHubsConnectionName") .AddEventHub("MyHub"); -var ExampleService = builder.AddProject() +var exampleService = builder.AddProject() .WithReference(eventHubs); ``` The `AddAzureEventHubs` method will read connection information from the AppHost's configuration (for example, from "user secrets") under the `ConnectionStrings:eventHubsConnectionName` config key. The `WithReference` method passes that connection information into a connection string named `eventHubsConnectionName` in the `ExampleService` project. +As of .NET Aspire 8.1, the Azure EventHubs extension for .NET Aspire supports launching a local emulator for EventHubs. You can use the emulator by applying the `RunAsEmulator()` extension method as follows: + +```csharp +var eventHubs = builder.AddAzureEventHubs("eventHubsConnectionName") + .RunAsEmulator() + .AddEventHub("MyHub"); +``` + +The emulator for Azure EventHubs results in two container resources being launched inside .NET Aspire derived from the name of the Event Hubs resource name. + > [!IMPORTANT] > Even though we are creating an Event Hub using the `AddEventHub` at the same time as the namespace, as of .NET Aspire version `preview-5`, the connection string will not include the `EntityPath` property, so the `EventHubName` property must be set in the settings callback for the preferred client. Future versions of Aspire will include the `EntityPath` property in the connection string and will not require the `EventHubName` property to be set in this scenario. diff --git a/docs/messaging/azure-web-pubsub-component.md b/docs/messaging/azure-web-pubsub-component.md new file mode 100644 index 0000000000..1445fac96b --- /dev/null +++ b/docs/messaging/azure-web-pubsub-component.md @@ -0,0 +1,198 @@ +--- +title: .NET Aspire Azure Web PubSub component +description: This article describes the .NET Aspire Azure Web PubSub component features and capabilities. +ms.topic: how-to +ms.date: 07/23/2024 +--- + +# .NET Aspire Azure Web PubSub component + +In this article, you learn how to use the .NET Aspire Azure Web PubSub component. The `Aspire.Azure.Messaging.WebPubSub` library offers options for registering an in the DI container for connecting to [Azure Web PubSub](/azure/azure-web-pubsub). + +## Prerequisites + +- Azure subscription: [create one for free](https://azure.microsoft.com/free/). +- An existing Azure Web PubSub service instance. For more information, see [Create a Web PubSub resource](/azure/azure-web-pubsub/howto-develop-create-instance). Alternatively, you can use a connection string, which isn't recommended in production environments. + +## Get started + +To get started with the .NET Aspire Azure Web PubSub component, install the [Aspire.Azure.Messaging.WebPubSub](https://www.nuget.org/packages/Aspire.Azure.Messaging.WebPubSub) NuGet package in the consuming client project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Azure.Messaging.WebPubSub +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). + +## Example usage + +In the _Program.cs_ file of your project, call the `AddAzureWebPubSubHub` extension method to register a `WebPubSubServiceClient` for use via the dependency injection container. The method takes a connection name parameter. + +```csharp +builder.AddAzureWebPubSubServiceClient("wps"); +``` + +You can then retrieve the `WebPubSubServiceClient` instance using dependency injection. For example, to retrieve the client from a service: + +```csharp +public class ExampleService(WebPubSubServiceClient client) +{ + // Use client... +} +``` + +For more information, see the [Azure.Messaging.WebPubSub documentation](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/webpubsub/Azure.Messaging.WebPubSub/README.md). + +## App host usage + +To add Azure Web PubSub hosting support to your , install the [Aspire.Hosting.Azure.WebPubSub](https://www.nuget.org/packages/Aspire.Hosting.Azure.WebPubSub) NuGet package in the [app host](xref:aspire/app-host) project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Azure.WebPubSub +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +In your app host project, add a Web PubSub connection and consume the connection using the following methods: + +```csharp +var webPubSub = builder.AddAzureWebPubSub("wps"); + +var exampleService = builder.AddProject() + .WithReference(webPubSub); +``` + +The `AddAzureWebPubSubHub` method reads connection information from the app host's configuration (for example, from "user secrets") under the `ConnectionStrings:wps` configuration key. The `WithReference` method passes that connection information into a connection string named `wps` in the `ExampleService` project. In the _Program.cs_ file of `ExampleService`, the connection can be consumed using: + +```csharp +builder.AddAzureWebPubSubServiceClient("wps"); +``` + +## Configuration + +The .NET Aspire Azure Web PubSub library provides multiple options to configure the Azure Web PubSub connection based on the requirements and conventions of your project. Note that either a `Endpoint` or a `ConnectionString` is a required to be supplied. + +### Use a connection string + +When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling `builder.AddAzureWebPubSubHub()`: + +```csharp +builder.AddAzureWebPubSubServiceClient( + "WebPubSubConnectionName", + "your_hub_name"); +``` + +And then the connection information will be retrieved from the `ConnectionStrings` configuration section. Two connection formats are supported: + +#### Use the service endpoint + +The recommended approach is to use the service endpoint, which works with the `AzureMessagingWebPubSubSettings.Credential` property to establish a connection. If no credential is configured, the [DefaultAzureCredential](/dotnet/api/azure.identity.defaultazurecredential) is used. + +```json +{ + "ConnectionStrings": { + "WebPubSubConnectionName": "https://xxx.webpubsub.azure.com" + } +} +``` + +#### Connection string + +Alternatively, a connection string can be used. + +```json +{ + "ConnectionStrings": { + "WebPubSubConnectionName": "Endpoint=https://xxx.webpubsub.azure.com;AccessKey==xxxxxxx" + } +} +``` + +### Use configuration providers + +The .NET Aspire Azure Web PubSub library supports [Microsoft.Extensions.Configuration](/dotnet/api/microsoft.extensions.configuration). It loads the `AzureMessagingWebPubSubSettings` and `WebPubSubServiceClientOptions` from configuration by using the `Aspire:Azure:Messaging:WebPubSub` key. Consider the example _appsettings.json_ that configures some of the options: + +```json +{ + "Aspire": { + "Azure": { + "Messaging": { + "WebPubSub": { + "DisableHealthChecks": true, + "HubName": "your_hub_name" + } + } + } + } +} +``` + +### Use inline delegates + +You can also pass the `Action configureSettings` delegate to set up some or all the options inline, for example to disable health checks from code: + +```csharp +builder.AddAzureWebPubSubServiceClient( + "wps", + settings => settings.DisableHealthChecks = true); +``` + +You can also setup the using the optional `Action> configureClientBuilder` parameter of the `AddAzureWebPubSubHub` method. For example, to set the client ID for this client: + +```csharp +builder.AddAzureWebPubSubServiceClient( + "wps", + configureClientBuilder: clientBuilder => + clientBuilder.ConfigureOptions(options => options.Retry.MaxRetries = 5)); +``` + +[!INCLUDE [component-health-checks](../includes/component-health-checks.md)] + +The .NET Aspire Azure Web PubSub component handles exposes a configurable health check that reports as _healthy_, when the client can successfully connect to the Azure Web PubSub service. + +[!INCLUDE [component-observability-and-telemetry](../includes/component-observability-and-telemetry.md)] + +### Logging + +The .NET Aspire Azure Web PubSub component uses the following log categories: + +- `Azure` +- `Azure.Core` +- `Azure.Identity` +- `Azure.Messaging.WebPubSub` + +### Tracing + +The .NET Aspire Azure Web PubSub component will emit the following tracing activities using OpenTelemetry: + +- "Azure.Messaging.WebPubSub.*" + +### Metrics + +The .NET Aspire Azure Web PubSub component currently doesn't support metrics by default due to limitations with the Azure SDK for .NET. If that changes in the future, this section will be updated to reflect those changes. + +## See also + +- [Azure Web PubSub](/azure/azure-web-pubsub/) +- [.NET Aspire components](../fundamentals/components-overview.md) +- [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) diff --git a/docs/real-time/snippets/signalr/SignalR.AppHost/SignalR.AppHost.csproj b/docs/real-time/snippets/signalr/SignalR.AppHost/SignalR.AppHost.csproj index e05915bb9f..8dd8bd2646 100644 --- a/docs/real-time/snippets/signalr/SignalR.AppHost/SignalR.AppHost.csproj +++ b/docs/real-time/snippets/signalr/SignalR.AppHost/SignalR.AppHost.csproj @@ -15,9 +15,9 @@ - - - + + + \ No newline at end of file diff --git a/docs/real-time/snippets/signalr/SignalR.ServiceDefaults/SignalR.ServiceDefaults.csproj b/docs/real-time/snippets/signalr/SignalR.ServiceDefaults/SignalR.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/real-time/snippets/signalr/SignalR.ServiceDefaults/SignalR.ServiceDefaults.csproj +++ b/docs/real-time/snippets/signalr/SignalR.ServiceDefaults/SignalR.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/search/elasticsearch-component.md b/docs/search/elasticsearch-component.md new file mode 100644 index 0000000000..387690e560 --- /dev/null +++ b/docs/search/elasticsearch-component.md @@ -0,0 +1,202 @@ +--- +title: .NET Aspire Elasticsearch component +description: This article describes the .NET Aspire Elasticsearch component features and capabilities. +ms.topic: how-to +ms.date: 07/23/2024 +--- + +# .NET Aspire Elasticsearch component + +In this article, you learn how to use the .NET Aspire Elasticsearch component. The `Aspire.Elastic.Clients.Elasticsearch` library registers a [ElasticsearchClient](https://github.com/elastic/elasticsearch-net) in the DI container for connecting to a Elasticsearch. It enables corresponding health check and telemetry. + +## Prerequisites + +- Elasticsearch cluster. +- Endpoint URI string for accessing the Elasticsearch API endpoint or a CloudId and an ApiKey from [Elastic Cloud](https://www.elastic.co/cloud) + +## Get started + +To get started with the .NET Aspire Elasticsearch component, install the [Aspire.Elastic.Clients.Elasticsearch](https://www.nuget.org/packages/Aspire.Elastic.Clients.Elasticsearch) NuGet package in the consuming client project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Elastic.Clients.Elasticsearch +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). + +## Example usage + +In the _Program.cs_ file of your project, call the `AddElasticsearchClient` extension method to register a `ElasticsearchClient` for use via the dependency injection container. The method takes a connection name parameter. + +```csharp +builder.AddElasticsearchClient("elasticsearch"); +``` + +## App host usage + +To model the Elasticsearch resource in the app host, install the [Aspire.Hosting.Elasticsearch](https://www.nuget.org/packages/Aspire.Hosting.Elasticsearch) NuGet package in the [app host](xref:aspire/app-host) project. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Elasticsearch +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +In the _Program.cs_ file of `AppHost`, register a Elasticsearch cluster and consume the connection using the following methods: + +```csharp +var elasticsearch = builder.AddElasticsearch("elasticsearch"); + +var myService = builder.AddProject() + .WithReference(elasticsearch); +``` + +The `WithReference` method configures a connection in the `MyService` project named `elasticsearch`. In the _Program.cs_ file of `MyService`, the Elasticsearch connection can be consumed using: + +```csharp +builder.AddElasticsearchClient("elasticsearch"); +``` + +## Configuration + +The .NET Aspire Elasticsearch client component provides multiple options to configure the server connection based on the requirements and conventions of your project. + +### Use a connection string + +When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling `builder.AddElasticsearchClient()`: + +```csharp +builder.AddElasticsearchClient("elasticsearch"); +``` + +And then the connection string will be retrieved from the `ConnectionStrings` configuration section: + +```json +{ + "ConnectionStrings": { + "elasticsearch": "http://elastic:password@localhost:27011" + } +} +``` + +### Use configuration providers + +The .NET Aspire Elasticsearch Client component supports [Microsoft.Extensions.Configuration](/dotnet/api/microsoft.extensions.configuration). It loads the `ElasticClientsElasticsearchSettings` from configuration by using the `Aspire:Elastic:Clients:Elasticsearch` key. Consider the following example _appsettings.json_ that configures some of the options: + +```json +{ + "Aspire": { + "Elastic": { + "Clients": { + "Elasticsearch": { + "Endpoint": "http://elastic:password@localhost:27011" + } + } + } + } +} +``` + +### Use inline delegates + +Also you can pass the `Action configureSettings` delegate to set up some or all the options inline, for example to set the API key from code: + +```csharp +builder.AddElasticsearchClient( + "elasticsearch", + settings => + settings.Endpoint = new Uri("http://elastic:password@localhost:27011")); +``` + +### Use a `CloudId` and an `ApiKey` with configuration providers + +When using [Elastic Cloud](https://www.elastic.co/cloud), you can provide the `CloudId` and `ApiKey` in `Aspire:Elastic:Clients:Elasticsearch` section when calling `builder.AddElasticsearchClient()`. + +```csharp +builder.AddElasticsearchClient("elasticsearch"); +``` + +Consider the following example _appsettings.json_ that configures the options: + +```json +{ + "Aspire": { + "Elastic": { + "Clients": { + "Elasticsearch": { + "ApiKey": "", + "CloudId": "" + } + } + } + } +} +``` + +### Use a `CloudId` and an `ApiKey` with inline delegates + +```csharp +builder.AddElasticsearchClient( + "elasticsearch", + settings => + { + settings.ApiKey = ""; + settings.CloudId = ""; + }); +``` + +[!INCLUDE [component-health-checks](../includes/component-health-checks.md)] + +The .NET Aspire Elasticsearch component uses the configured client to perform a `PingAsync`. If the result is an HTTP 200 OK, the health check is considered healthy, otherwise it's unhealthy. Likewise, if there's an exception, the health check is considered unhealthy with the error propagating through the health check failure. + +[!INCLUDE [component-observability-and-telemetry](../includes/component-observability-and-telemetry.md)] + +### Tracing + +The .NET Aspire Elasticsearch component will emit the following tracing activities using OpenTelemetry: + +- `Elastic.Transport` + +## See also + +- [Elasticsearch .NET](https://github.com/elastic/elasticsearch-net) +- [.NET Aspire components](../fundamentals/components-overview.md) +- [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) + + \ No newline at end of file diff --git a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.AppHost/AspireStorage.AppHost.csproj b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.AppHost/AspireStorage.AppHost.csproj index f69dddc918..5bc9d73a74 100644 --- a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.AppHost/AspireStorage.AppHost.csproj +++ b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.AppHost/AspireStorage.AppHost.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.ServiceDefaults/AspireStorage.ServiceDefaults.csproj b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.ServiceDefaults/AspireStorage.ServiceDefaults.csproj index b8d847c913..84e9efc96f 100644 --- a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.ServiceDefaults/AspireStorage.ServiceDefaults.csproj +++ b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.ServiceDefaults/AspireStorage.ServiceDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.Web/AspireStorage.Web.csproj b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.Web/AspireStorage.Web.csproj index 41c6bc2ddd..21754bb58f 100644 --- a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.Web/AspireStorage.Web.csproj +++ b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.Web/AspireStorage.Web.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.WorkerService/AspireStorage.WorkerService.csproj b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.WorkerService/AspireStorage.WorkerService.csproj index f65e12cee3..9d0b8aa205 100644 --- a/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.WorkerService/AspireStorage.WorkerService.csproj +++ b/docs/storage/snippets/tutorial/AspireStorage/AspireStorage.WorkerService/AspireStorage.WorkerService.csproj @@ -8,7 +8,7 @@ - + diff --git a/docs/toc.yml b/docs/toc.yml index 6b327f8576..bb9adc810d 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -12,8 +12,6 @@ items: href: get-started/build-your-first-aspire-app.md - name: Tutorial - Add .NET Aspire to an existing .NET app href: get-started/add-aspire-existing-app.md - - name: Sample - Add Node.js apps to a .NET Aspire project - href: get-started/build-aspire-apps-with-nodejs.md - name: Setup and tooling href: fundamentals/setup-tooling.md - name: What's new in .NET Aspire @@ -24,6 +22,13 @@ items: - name: Overview displayName: orchestration,aspire apphost,aspire app href: fundamentals/app-host-overview.md + - name: Orchestrate Node.js apps in .NET Aspire + href: get-started/build-aspire-apps-with-nodejs.md + - name: Orchestrate Python apps in .NET Aspire + href: get-started/build-aspire-apps-with-python.md + - name: Add Dockerfiles to the app model + href: app-host/withdockerfile.md + displayName: dockerfile,docker - name: Networking overview displayName: inner loop,local networking href: fundamentals/networking-overview.md @@ -135,6 +140,18 @@ items: - name: Azure Table Storage displayName: table storage,azure storage href: storage/azure-storage-tables-component.md + - name: Azure Web PubSub + displayName: web pubsub,real-time,messaging + href: messaging/azure-web-pubsub-component.md + - name: Elasticsearch + displayName: elasticsearch,search + href: search/elasticsearch-component.md + - name: Keycloak + displayName: security,openid connect,single sign-on,sso,identity,federation,account management + href: authentication/keycloak-component.md + - name: Milvus + displayName: milvus,database + href: database/milvus-component.md - name: MongoDB displayName: mongodb,database href: database/mongodb-component.md @@ -165,14 +182,23 @@ items: - name: Overview href: caching/stackexchange-redis-caching-overview.md - name: Redis caching - displayName: caching href: caching/stackexchange-redis-component.md - name: Redis distributed cache - displayName: distributed caching href: caching/stackexchange-redis-distributed-caching-component.md - name: Redis output caching - displayName: output caching href: caching/stackexchange-redis-output-caching-component.md + - name: Redis caching (Garnet) + href: caching/stackexchange-redis-component.md?pivots=garnet + - name: Redis distributed cache (Garnet) + href: caching/stackexchange-redis-distributed-caching-component.md?pivots=garnet + - name: Redis output caching (Garnet) + href: caching/stackexchange-redis-output-caching-component.md?pivots=garnet + - name: Redis caching (Valkey) + href: caching/stackexchange-redis-component.md?pivots=valkey + - name: Redis distributed cache (Valkey) + href: caching/stackexchange-redis-distributed-caching-component.md?pivots=valkey + - name: Redis output caching (Valkey) + href: caching/stackexchange-redis-output-caching-component.md?pivots=valkey - name: SQL Server items: - name: SQL Database - EF Core diff --git a/docs/zones/zone-pivot-groups.yml b/docs/zones/zone-pivot-groups.yml index 6bc5cf7074..ff2b0b95c6 100644 --- a/docs/zones/zone-pivot-groups.yml +++ b/docs/zones/zone-pivot-groups.yml @@ -43,4 +43,13 @@ groups: title: Visual Studio Code - id: dotnet-cli title: .NET CLI - +- id: resp-host + title: RESP Host + prompt: Choose a RESP Hosting resource type + pivots: + - id: redis + title: Redis + - id: garnet + title: Garnet + - id: valkey + title: Valkey