diff --git a/docs/database/includes/media/ads-connect-details.png b/docs/database/includes/media/ads-connect-details.png new file mode 100644 index 0000000000..2fbe24b1eb Binary files /dev/null and b/docs/database/includes/media/ads-connect-details.png differ diff --git a/docs/database/includes/media/ads-connected.png b/docs/database/includes/media/ads-connected.png new file mode 100644 index 0000000000..2a726f2e7c Binary files /dev/null and b/docs/database/includes/media/ads-connected.png differ diff --git a/docs/database/includes/media/ads-new-connection.png b/docs/database/includes/media/ads-new-connection.png new file mode 100644 index 0000000000..7427011a5f Binary files /dev/null and b/docs/database/includes/media/ads-new-connection.png differ diff --git a/docs/database/includes/media/ssms-connected.png b/docs/database/includes/media/ssms-connected.png new file mode 100644 index 0000000000..48994fe20b Binary files /dev/null and b/docs/database/includes/media/ssms-connected.png differ diff --git a/docs/database/includes/media/ssms-new-connection.png b/docs/database/includes/media/ssms-new-connection.png new file mode 100644 index 0000000000..7df212e180 Binary files /dev/null and b/docs/database/includes/media/ssms-new-connection.png differ diff --git a/docs/database/includes/sql-app-host.md b/docs/database/includes/sql-app-host.md index e909815666..8745a8420c 100644 --- a/docs/database/includes/sql-app-host.md +++ b/docs/database/includes/sql-app-host.md @@ -1,4 +1,8 @@ -To model the SqlServer resource in the app host, install the [📦 Aspire.Hosting.SqlServer) NuGet package in the [app host](xref:dotnet/aspire/app-host](https://www.nuget.org/packages/Aspire.Hosting.SqlServer) NuGet package in the [app host](xref:dotnet/aspire/app-host) project. +--- +ms.topic: include +--- + +The SQL Server hosting integration models the server as the type and the database as the type. To access these types and APIs, add the [📦 Aspire.Hosting.SqlServer](https://www.nuget.org/packages/Aspire.Hosting.SqlServer) NuGet package in the [app host](xref:dotnet/aspire/app-host) project. ### [.NET CLI](#tab/dotnet-cli) @@ -15,4 +19,149 @@ dotnet add package Aspire.Hosting.SqlServer --- -In your app host project, register a SqlServer database and consume the connection using the following methods: +For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). + +### Add SQL Server resource and database resource + +In your app host project, call to add and return a SQL Server resource builder. Chain a call to the returned resource build to , to add SQL Server database resource. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithLifetime(ContainerLifetime.Persistent); + +var db = sql.AddDatabase("database"); + +builder.AddProject() + .WithReference(db); + +// After adding all resources, run the app... +``` + +> [!NOTE] +> The SQL Server container is slow to start, so it's best to use a _persistent_ lifetime to avoid unnecessary restarts. For more information, see [Container resource lifetime](../../fundamentals/app-host-overview.md#container-resource-lifetime). + +When .NET Aspire adds a container image to the app host, as shown in the preceding example with the `mcr.microsoft.com/mssql/server` image, it creates a new SQL Server instance on your local machine. A reference to your SQL Server resource builder (the `sql` variable) is used to add a database. The database is named `database` and then added to the `ExampleProject`. The SQL Server resource includes default credentials with a `username` of `sa` and a random `password` generated using the method. + +When the app host runs, the password is stored in the app host's secret store. It's added to the `Parameters` section, for example: + +```json +{ + "Parameters:sql-password": "" +} +``` + +The name of the parameter is `sql-password`, but really it's just formatting the resource name with a `-password` suffix. For more information, see [Safe storage of app secrets in development in ASP.NET Core](/aspnet/core/security/app-secrets) and [Add SQL Server resource with parameters](#add-sql-server-resource-with-parameters). + +The method configures a connection in the `ExampleProject` named `database`. + +> [!TIP] +> If you'd rather connect to an existing SQL Server, call instead. For more information, see [Reference existing resources](../../fundamentals/app-host-overview.md#reference-existing-resources). + +### Add SQL Server resource with data volume + +To add a data volume to the SQL Server resource, call the method on the SQL Server resource: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithDataVolume(); + +var db = sql.AddDatabase("database"); + +builder.AddProject() + .WithReference(db); + +// After adding all resources, run the app... +``` + +The data volume is used to persist the SQL Server data outside the lifecycle of its container. The data volume is mounted at the `/var/opt/mssql` path in the SQL Server container and when a `name` parameter isn't provided, the name is generated at random. For more information on data volumes and details on why they're preferred over [bind mounts](#add-sql-server-resource-with-data-bind-mount), see [Docker docs: Volumes](https://docs.docker.com/engine/storage/volumes). + +> [!WARNING] +> The password is stored in the data volume. When using a data volume and if the password changes, it will not work until you delete the volume. + +### Add SQL Server resource with data bind mount + +To add a data bind mount to the SQL Server resource, call the method: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddSqlServer("sql") + .WithDataBindMount(source: @"C:\SqlServer\Data"); + +var db = sql.AddDatabase("database"); + +builder.AddProject() + .WithReference(db); + +// After adding all resources, run the app... +``` + +[!INCLUDE [data-bind-mount-vs-volumes](../../includes/data-bind-mount-vs-volumes.md)] + +Data bind mounts rely on the host machine's filesystem to persist the SQL Server data across container restarts. The data bind mount is mounted at the `C:\SqlServer\Data` on Windows (or `/SqlServer/Data` on Unix) path on the host machine in the SQL Server container. For more information on data bind mounts, see [Docker docs: Bind mounts](https://docs.docker.com/engine/storage/bind-mounts). + +### Add SQL Server resource with parameters + +When you want to explicitly provide the password used by the container image, you can provide these credentials as parameters. Consider the following alternative example: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var password = builder.AddParameter("password", secret: true); + +var sql = builder.AddSqlServer("sql", password); +var db = sql.AddDatabase("database"); + +builder.AddProject() + .WithReference(db); + +// After adding all resources, run the app... +``` + +For more information on providing parameters, see [External parameters](../../fundamentals/external-parameters.md). + +### Connect to database resources + +When the .NET Aspire app host runs, the server's database resources can be accessed from external tools, such as [SQL Server Management Studio (SSMS)](/sql/ssms/download-sql-server-management-studio-ssms) or [Azure Data Studio](/azure-data-studio/download-azure-data-studio). The connection string for the database resource is available in the dependent resources environment variables and is accessed using the [.NET Aspire dashboard: Resource details](../../fundamentals/dashboard/explore.md#resource-details) pane. The environment variable is named `ConnectionStrings__{name}` where `{name}` is the name of the database resource, in this example it's `database`. Use the connection string to connect to the database resource from external tools. Imagine that you have a database named `todos` with a single `dbo.Todos` table. + +#### [SQL Server Management Studio](#tab/ssms) + +To connect to the database resource from SQL Server Management Studio, follow these steps: + +1. Open SSMS. +1. In the **Connect to Server** dialog, select the **Additional Connection Parameters** tab. +1. Paste the connection string into the **Additional Connection Parameters** field and select **Connect**. + + :::image type="content" source="media/ssms-new-connection.png" lightbox="media/ssms-new-connection.png" alt-text="SQL Server Management Studio: Connect to Server dialog."::: + +1. If you're connected, you can see the database resource in the **Object Explorer**: + + :::image type="content" source="media/ssms-connected.png" lightbox="media/ssms-connected.png" alt-text="SQL Server Management Studio: Connected to database."::: + +For more information, see [SQL Server Management Studio: Connect to a server](/sql/ssms/quickstarts/ssms-connect-query-sql-server). + +#### [Azure Data Studio](#tab/azure-data-studio) + +To connect to the database resource from Azure Data Studio, follow these steps: + +1. Open Azure Data Studio. +1. Select the **New** dropdown and choose **New connection**. + + :::image type="content" source="media/ads-new-connection.png" lightbox="media/ads-new-connection.png" alt-text="Azure Data Studio: New / New connection screen capture."::: + +1. Change the **Input type** to **Connection string** and paste the connection string into the **Connection string** field. +1. Select **Connect**. + + :::image type="content" source="media/ads-connect-details.png" lightbox="media/ads-connect-details.png" alt-text="Azure Data Studio: Connection string input details."::: + +1. If you're connected, you can see the database resource in the active tab: + + :::image type="content" source="media/ads-connected.png" lightbox="media/ads-connected.png" alt-text="Azure Data Studio: Connected to database."::: + +For more information, see [Azure Data Studio: Connect to SQL Server](/azure-data-studio/quickstart-sql-server). + +--- diff --git a/docs/database/sql-server-integration.md b/docs/database/sql-server-integration.md index a6cf483883..561704d336 100644 --- a/docs/database/sql-server-integration.md +++ b/docs/database/sql-server-integration.md @@ -1,27 +1,29 @@ --- title: .NET Aspire SQL Server integration -description: This article describes the .NET Aspire SQL Server integration. -ms.topic: how-to -ms.date: 08/12/2024 +description: Learn how to use the .NET Aspire SQL Server integration, which includes both hosting and client integrations. +ms.date: 11/20/2024 +uid: database/sql-server-integration --- # .NET Aspire SQL Server integration -In this article, you learn how to use the .NET Aspire SQL Server integration. The `Aspire.Microsoft.Data.SqlClient` library: +[!INCLUDE [includes-hosting-and-client](../includes/includes-hosting-and-client.md)] -- Registers a scoped factory in the DI container for connecting Azure SQL, MS SQL database. -- Automatically configures the following: - - Connection pooling to efficiently managed HTTP requests and database connections - - Automatic retries to increase app resiliency - - Health checks, logging and telemetry to improve app monitoring and diagnostics +[SQL Server](https://www.microsoft.com/en-us/sql-server) is a relational database management system developed by Microsoft. The .NET Aspire SQL Server integration enables you to connect to existing SQL Server instances or create new instances from .NET with the [`mcr.microsoft.com/mssql/server` container image](https://hub.docker.com/_/microsoft-mssql-server). -## Prerequisites +## Hosting integration -- An [Azure SQL Database](/azure/azure-sql/database) or [SQL Server](/sql/sql-server) database and the connection string for accessing the database. +[!INCLUDE [sql-app-host](includes/sql-app-host.md)] + +### Hosting integration health checks + +The SQL Server hosting integration automatically adds a health check for the SQL Server resource. The health check verifies that the SQL Server is running and that a connection can be established to it. -## Get started +The hosting integration relies on the [📦 AspNetCore.HealthChecks.SqlServer](https://www.nuget.org/packages/AspNetCore.HealthChecks.SqlServer) NuGet package. -To get started with the .NET Aspire SQL Server integration, install the [📦 Aspire.Microsoft.Data.SqlClient](https://www.nuget.org/packages/Aspire.Microsoft.Data.SqlClient) NuGet package in the client-consuming project, i.e., the project for the application that uses the SQL Server client. +## Client integration + +To get started with the .NET Aspire SQL Server client integration, install the [📦 Aspire.Microsoft.Data.SqlClient](https://www.nuget.org/packages/Aspire.Microsoft.Data.SqlClient) NuGet package in the client-consuming project, that is, the project for the application that uses the SQL Server client. The SQL Server client integration registers a instance that you can use to interact with SQL Server. ### [.NET CLI](#tab/dotnet-cli) @@ -38,164 +40,131 @@ dotnet add package Aspire.Microsoft.Data.SqlClient --- -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 +### Add SQL Server client -In the _:::no-loc text="Program.cs":::_ file of your client-consuming project, call the extension to register a for use via the dependency injection container. +In the _:::no-loc text="Program.cs":::_ file of your client-consuming project, call the extension method on any to register a `SqlConnection` for use via the dependency injection container. The method takes a connection name parameter. ```csharp -builder.AddSqlServerClient("sqldb"); +builder.AddSqlServerClient(connectionName: "database"); ``` -To retrieve your `SqlConnection` object an example service: +> [!TIP] +> The `connectionName` parameter must match the name used when adding the SQL Server database resource in the app host project In other words, when you call `AddDatabase` and provide a name of `myDatabase` that same name should be used when calling `AddSqlServerClient`. For more information, see [Add SQL Server resource and database resource](#add-sql-server-resource-and-database-resource). + +You can then retrieve the instance using dependency injection. For example, to retrieve the connection from an example service: ```csharp -public class ExampleService(SqlConnection client) +public class ExampleService(SqlConnection connection) { - // Use client... + // Use connection... } ``` -After adding a `SqlConnection`, you can get the scoped [SqlConnection](/dotnet/api/microsoft.data.sqlclient.sqlconnection) instance using DI. +For more information on dependency injection, see [.NET dependency injection](/dotnet/core/extensions/dependency-injection). -## App host usage +### Add keyed SQL Server client -[!INCLUDE [sql-app-host](includes/sql-app-host.md)] +There might be situations where you want to register multiple `SqlConnection` instances with different connection names. To register keyed SQL Server clients, call the method: ```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var sql = builder.AddSqlServer("sql"); -var sqldb = sql.AddDatabase("sqldb"); - -var myService = builder.AddProject() - .WithReference(sqldb); +builder.AddKeyedSqlServerClient(name: "mainDb"); +builder.AddKeyedSqlServerClient(name: "loggingDb"); ``` -When you want to explicitly provide a root SQL password, you can provide it as a parameter. Consider the following alternative example: +> [!IMPORTANT] +> When using keyed services, it's expected that your SQL Server resource configured two named databases, one for the `mainDb` and one for the `loggingDb`. -```csharp -var password = builder.AddParameter("password", secret: true); - -var sql = builder.AddSqlServer("sql", password); -var sqldb = sql.AddDatabase("sqldb"); +Then you can retrieve the `SqlConnection` instances using dependency injection. For example, to retrieve the connection from an example service: -var myService = builder.AddProject() - .WithReference(sqldb); +```csharp +public class ExampleService( + [FromKeyedServices("mainDb")] SqlConnection mainDbConnection, + [FromKeyedServices("loggingDb")] SqlConnection loggingDbConnection) +{ + // Use connections... +} ``` -For more information, see [External parameters](../fundamentals/external-parameters.md). +For more information on keyed services, see [.NET dependency injection: Keyed services](/dotnet/core/extensions/dependency-injection#keyed-services). -## Configuration +### Configuration -The .NET Aspire SQL Server integration provides multiple configuration approaches and options to meet the requirements and conventions of your project. +The .NET Aspire SQL Server integration provides multiple options to configure the connection based on the requirements and conventions of your project. -### Use a connection string +#### Use a connection string -When using a connection string from the `ConnectionStrings` configuration section, you provide the name of the connection string when calling `AddSqlServerClient`: +When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling the method: ```csharp -builder.AddSqlServerClient("myConnection"); +builder.AddSqlServerClient(connectionName: "sql"); ``` -The connection string is retrieved from the `ConnectionStrings` configuration section: +Then the connection string is retrieved from the `ConnectionStrings` configuration section: ```json { "ConnectionStrings": { - "myConnection": "Data Source=myserver;Initial Catalog=master" + "database": "Data Source=myserver;Initial Catalog=master" } } ``` -For more information, see the [ConnectionString](/dotnet/api/system.data.sqlclient.sqlconnection.connectionstring#remarks). +For more information on how to format this connection string, see the [ConnectionString](/dotnet/api/system.data.sqlclient.sqlconnection.connectionstring#remarks). -### Use configuration providers +#### Use configuration providers -The .NET Aspire SQL Server supports . It loads the `MicrosoftDataSqlClientSettings` from configuration files such as _:::no-loc text="appsettings.json":::_ by using the `Aspire:SqlServer:SqlClient` key. If you have set up your configurations in the `Aspire:SqlServer:SqlClient` section, you can just call the method without passing any parameter. - -The following example shows an _:::no-loc text="appsettings.json":::_ file that configures some of the available options: +The .NET Aspire SQL Server integration supports . It loads the from configuration by using the `Aspire:Microsoft:Data:SqlClient` key. The following snippet is an example of a _:::no-loc text="appsettings.json":::_ file that configures some of the options: ```json { "Aspire": { - "SqlServer": { - "SqlClient": { - "ConnectionString": "YOUR_CONNECTIONSTRING", - "DisableHealthChecks": false, - "DisableMetrics": true + "Microsoft": { + "Data": { + "SqlClient": { + "ConnectionString": "YOUR_CONNECTIONSTRING", + "DisableHealthChecks": false, + "DisableMetrics": true + } } } } } ``` -### Use inline configurations +For the complete SQL Server client integration JSON schema, see [Aspire.Microsoft.Data.SqlClient/ConfigurationSchema.json](https://github.com/dotnet/aspire/blob/v8.2.2/src/Components/Aspire.Microsoft.Data.SqlClient/ConfigurationSchema.json). -You can also pass the `Action` delegate to set up some or all the options inline, for example to turn off the `DisableMetrics`: +#### Use inline delegates -```csharp -builder.AddSqlServerClient( - static settings => settings.DisableMetrics = true); -``` - -### Configuring connections to multiple databases - -If you want to add more than one `SqlConnection` you could use named instances. The json configuration would look like: - -```json -{ - "Aspire": { - "SqlServer": { - "SqlClient": { - "INSTANCE_NAME": { - "ServiceUri": "YOUR_URI", - "DisableHealthChecks": true - } - } - } - } -} -``` - -To load the named configuration section from the json config call the `AddSqlServerClient` method by passing the `INSTANCE_NAME`. +Also you can pass the `Action configureSettings` delegate to set up some or all the options inline, for example to disable health checks from code: ```csharp -builder.AddSqlServerClient("INSTANCE_NAME"); +builder.AddSqlServerClient( + "database", + static settings => settings.DisableHealthChecks = true); ``` -### Configuration options - -Here are the configurable options with corresponding default values: - -| Name | Description | -|-----------------------|---------------------------------------------------------------------------------------| -| `ConnectionString` | The connection string of the SQL Server database to connect to. | -| `DisableHealthChecks` | A boolean value that indicates whether the database health check is disabled or not. | -| `DisableTracing` | A boolean value that indicates whether the OpenTelemetry tracing is disabled or not. | -| `DisableMetrics` | A boolean value that indicates whether the OpenTelemetry metrics are disabled or not. | +### Client integration health checks -[!INCLUDE [integration-health-checks](../includes/integration-health-checks.md)] +By default, .NET Aspire integrations enable [health checks](../fundamentals/health-checks.md) for all services. For more information, see [.NET Aspire integrations overview](../fundamentals/integrations-overview.md). -By default, the .NET Aspire Sql Server integration handles the following: +The .NET Aspire SQL Server integration: -- Adds the [`SqlServerHealthCheck`](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/blob/master/src/HealthChecks.NpgSql/NpgSqlHealthCheck.cs), which verifies that a connection can be made commands can be run against the SQL Database. -- Integrates with the `/health` HTTP endpoint, which specifies all registered health checks must pass for app to be considered ready to accept traffic +- Adds the health check when is `false`, which attempts to connect to the SQL Server. +- Integrates with the `/health` HTTP endpoint, which specifies all registered health checks must pass for app to be considered ready to accept traffic. [!INCLUDE [integration-observability-and-telemetry](../includes/integration-observability-and-telemetry.md)] -### Logging +#### Logging -The .NET Aspire SQL Server integration currently doesn't enable logging by default due to limitations of the `SqlClient`. +The .NET Aspire SQL Server integration currently doesn't enable logging by default due to limitations of the . -### Tracing +#### Tracing -The .NET Aspire SQL Server integration will emit the following Tracing activities using OpenTelemetry: +The .NET Aspire SQL Server integration emits the following tracing activities using OpenTelemetry: -- "OpenTelemetry.Instrumentation.SqlClient" +- `OpenTelemetry.Instrumentation.SqlClient` -### Metrics +#### Metrics The .NET Aspire SQL Server integration will emit the following metrics using OpenTelemetry: @@ -221,5 +190,6 @@ The .NET Aspire SQL Server integration will emit the following metrics using Ope - [Azure SQL Database](/azure/azure-sql/database) - [SQL Server](/sql/sql-server) +- [.NET Aspire database containers sample](/samples/dotnet/aspire-samples/aspire-database-containers/) - [.NET Aspire integrations](../fundamentals/integrations-overview.md) - [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) diff --git a/docs/fundamentals/app-host-overview.md b/docs/fundamentals/app-host-overview.md index 0329b59c6e..0ed79abec6 100644 --- a/docs/fundamentals/app-host-overview.md +++ b/docs/fundamentals/app-host-overview.md @@ -242,6 +242,19 @@ These commands are used instead of `podman run` to manage attached container net Beyond the base resource types, , , and , .NET Aspire provides extension methods to add common resources to your app model. For more information, see [Hosting integrations](integrations-overview.md#hosting-integrations). +#### Container resource lifetime + +By default, container resources use the _session_ container lifetime. This means that every time the app host process is started, the container is created and started. When the app host stops, the container is stopped and removed. Container resources can opt-in to a _persistent_ lifetime to avoid unnecessary restarts and leverage persisted container state. To achieve this, chain a call the API and pass : + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var ollama = builder.AddContainer("ollama", "ollama/ollama") + .WithLifetime(ContainerLifetime.Persistent); +``` + +The preceding code adds a container resource named "ollama" with the image "ollama/ollama" and a persistent lifetime. + ### Connection string and endpoint references It's common to express dependencies between project resources. Consider the following example code: