diff --git a/docs/database/oracle-entity-framework-integration.md b/docs/database/oracle-entity-framework-integration.md index ba1f15e319..5f4a211fb5 100644 --- a/docs/database/oracle-entity-framework-integration.md +++ b/docs/database/oracle-entity-framework-integration.md @@ -1,27 +1,30 @@ --- -title: Oracle Entity Framework Component -description: Oracle Entity Framework Component -ms.date: 08/12/2024 +title: .NET Aspire Oracle Entity Framework Core integration +description: Learn how to use the .NET Aspire Oracle Entity Framework Core integration, which includes both hosting and client integrations. +ms.date: 01/21/2025 +uid: database/oracle-entity-framework-integration --- -# .NET Aspire Oracle Entity Framework Component +# .NET Aspire Oracle Entity Framework Core integration -In this article, you learn how to use the .NET Aspire Oracle Entity Framework Core integration. The `Aspire.Oracle.EntityFrameworkCore` library is used to register a as a singleton in the DI container for connecting to Oracle databases. It also enables connection pooling, retries, health checks, logging and telemetry. +[!INCLUDE [includes-hosting-and-client](../includes/includes-hosting-and-client.md)] -## Get started +[Oracle Database](https://www.oracle.com/database/technologies/) is a widely-used relational database management system owned and developed by Oracle. The .NET Aspire Oracle Entity Framework Core integration enables you to connect to existing Oracle servers or create new servers from .NET with the [container-registry.orcale.com/databse/free](https://container-registry.oracle.com/ords/f?p=113:4:5999388133692:::RP,4:P4_REPOSITORY,AI_REPOSITORY,P4_REPOSITORY_NAME,AI_REPOSITORY_NAME:1863,1863,Oracle%20Database%20Free,Oracle%20Database%20Free&cs=3L7x5hgm9Co0WJN-3xZTrFJkDyCZKiS8wlK1jg7nU2yE65gVGYh4WbMLzmX59tAHoLwbwWeAz-kjraRQzB1V5TA) container image. -You need an Oracle database and connection string for accessing the database. To get started with the .NET Aspire Oracle Entity Framework Core integration, install the [📦 Aspire.Oracle.EntityFrameworkCore](https://www.nuget.org/packages/Aspire.Oracle.EntityFrameworkCore) NuGet package in the consuming client project. +## Hosting integration + +The .NET Aspire Oracle hosting integration models the server as the type and the database as the type. To access these types and APIs, add the [📦 Aspire.Hosting.Oracle](https://www.nuget.org/packages/Aspire.Hosting.Oracle) NuGet package in the [app host](xref:dotnet/aspire/app-host) project. ### [.NET CLI](#tab/dotnet-cli) ```dotnetcli -dotnet add package Aspire.Oracle.EntityFrameworkCore +dotnet add package Aspire.Hosting.Oracle ``` ### [PackageReference](#tab/package-reference) ```xml - ``` @@ -29,97 +32,193 @@ dotnet add package Aspire.Oracle.EntityFrameworkCore 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 Oracle server and database resources -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 your app host project, call to add and return an Oracle server resource builder. Chain a call to the returned resource builder to , to add an Oracle database to the server resource: ```csharp -builder.AddOracleDatabaseDbContext("oracledb"); +var builder = DistributedApplication.CreateBuilder(args); + +var oracle = builder.AddOracle("oracle") + .WithLifetime(ContainerLifetime.Persistent); + +var oracledb = oracle.AddDatabase("oracledb"); + +builder.AddProject() + .WithReference(oracledb); + .WaitFor(oracledb); + +// After adding all resources, run the app... ``` -You can then retrieve the instance using dependency injection. For example, to retrieve the client from a service: +> [!NOTE] +> The Oracle database container can be 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 `container-registry.oracle.com/database/free` image, it creates a new Oracle server on your local machine. A reference to your Oracle resource builder (the `oracle` variable) is used to add a database. The database is named `oracledb` and then added to the `ExampleProject`. The Oracle resource includes a random `password` generated using the method. + +The method configures a connection in the `ExampleProject` named `"oracledb"`. For more information, see [Container resource lifecycle](../fundamentals/app-host-overview.md#container-resource-lifecycle). + +> [!TIP] +> If you'd rather connect to an existing Oracle server, call instead. For more information, see [Reference existing resources](../fundamentals/app-host-overview.md#reference-existing-resources). + +### Add Oracle resource with password parameter + +The Oracle resource includes default credentials with a random password. Oracle supports configuration-based default passwords by using the environment variable `ORACLE_PWD`. When you want to provide a password explicitly, you can provide it as a parameter: ```csharp -public class ExampleService(MyDbContext context) +var password = builder.AddParameter("password", secret: true); + +var oracle = builder.AddOracle("oracle", password) + .WithLifetime(ContainerLifetime.Persistent); + +var oracledb = oracle.AddDatabase("oracledb"); + +var myService = builder.AddProject() + .WithReference(oracledb) + .WaitFor(oracledb); +``` + +The preceding code gets a parameter to pass to the `AddOracle` API, and internally assigns the parameter to the `ORACLE_PWD` environment variable of the Oracle container. The `password` parameter is usually specified as a _user secret_: + +```json { - // Use context... + "Parameters": { + "password": "Non-default-P@ssw0rd" + } } ``` -You might also need to configure specific options of Oracle database, or register a `DbContext` in other ways. In this case call the `EnrichOracleDatabaseDbContext` extension method, for example: +For more information, see [External parameters](../fundamentals/external-parameters.md). + +### Add Oracle resource with data volume + +To add a data volume to the Oracle resource, call the method: ```csharp -var connectionString = builder.Configuration.GetConnectionString("oracledb"); +var builder = DistributedApplication.CreateBuilder(args); + +var oracle = builder.AddOracle("oracle") + .WithDataVolume() + .WithLifetime(ContainerLifetime.Persistent); + +var oracledb = oracle.AddDatabase("oracle"); -builder.Services.AddDbContextPool( - dbContextOptionsBuilder => dbContextOptionsBuilder.UseOracle(connectionString)); +builder.AddProject() + .WithReference(oracledb) + .WaitFor(oracledb); -builder.EnrichOracleDatabaseDbContext(); +// After adding all resources, run the app... ``` -## App host usage +The data volume is used to persist the Oracle data outside the lifecycle of its container. The data volume is mounted at the `/opt/oracle/oradata` path in the Oracle 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-oracle-resource-with-data-bind-mount), see [Docker docs: Volumes](https://docs.docker.com/engine/storage/volumes). -To model the Oracle server resource in the app host, install the [📦 Aspire.Hosting.Oracle](https://www.nuget.org/packages/Aspire.Hosting.Oracle) NuGet package in the [app host](xref:dotnet/aspire/app-host) project. +> [!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 Oracle resource with data bind mount + +To add a data bind mount to the Oracle resource, call the method: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +var oracle = builder.AddOracle("oracle") + .WithDataBindMount(source: @"C:\Oracle\Data"); + +var oracledb = oracle.AddDatabase("oracledb"); + +builder.AddProject() + .WithReference(oracledb) + .WaitFor(oracledb); + +// 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 Oracle data across container restarts. The data bind mount is mounted at the `C:\Oracle\Data` on Windows (or `/Oracle/Data` on Unix) path on the host machine in the Oracle container. For more information on data bind mounts, see [Docker docs: Bind mounts](https://docs.docker.com/engine/storage/bind-mounts). + +### Hosting integration health checks + +The Oracle hosting integration automatically adds a health check for the Oracle resource. The health check verifies that the Oracle server is running and that a connection can be established to it. + +The hosting integration relies on the [📦 AspNetCore.HealthChecks.Oracle](https://www.nuget.org/packages/AspNetCore.HealthChecks.Oracle) NuGet package. + +## Client integration + +You need an Oracle database and connection string for accessing the database. To get started with the .NET Aspire Oracle client integration, install the [📦 Aspire.Oracle.EntityFrameworkCore](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 Oracle client. The Oracle client integration registers a instance that you can use to interact with Oracle. ### [.NET CLI](#tab/dotnet-cli) ```dotnetcli -dotnet add package Aspire.Hosting.Oracle +dotnet add package Aspire.Oracle.EntityFrameworkCore ``` ### [PackageReference](#tab/package-reference) ```xml - ``` --- -In your app host project, register an Oracle container and consume the connection using the following methods: +### Add Oracle client + +In the _:::no-loc text="Program.cs":::_ file of your client-consuming project, call the extension method on any to register a `DbContext` for use via the dependency injection container. The method takes a connection name parameter. ```csharp -var builder = DistributedApplication.CreateBuilder(args); +builder.AddOracleDatabaseDbContext(connectionName: "oracledb"); +``` -var oracle = builder.AddOracle("oracle"); -var oracledb = oracle.AddDatabase("oracledb"); +> [!TIP] +> The `connectionName` parameter must match the name used when adding the Oracle database resource in the app host project. In other words, when you call `AddDatabase` and provide a name of `oracledb` that same name should be used when calling `AddOracleDatabaseDbContext`. For more information, see [Add Oracle server and database resources](#add-oracle-server-and-database-resources). -var myService = builder.AddProject() - .WithReference(oracledb); +You can then retrieve the instance using dependency injection. For example, to retrieve the connection from an example service: + +```csharp +public class ExampleService(ExampleDbContext context) +{ + // Use database context... +} ``` -When you want to explicitly provide a password, you can provide it as a parameter. Consider the following alternative example: +For more information on dependency injection, see [.NET dependency injection](/dotnet/core/extensions/dependency-injection). -```csharp -var password = builder.AddParameter("password", secret: true); +### Add Oracle database context with enrichment -var oracle = builder.AddOracle("oracle", password); -var oracledb = oracle.AddDatabase("oracledb"); +To enrich the `DbContext` with additional services, such as automatic retries, health checks, logging and telemetry, call the method: -var myService = builder.AddProject() - .WithReference(oracledb); +```csharp +builder.EnrichOracleDatabaseDbContext( + connectionName: "oracledb", + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); ``` -For more information, see [External parameters](../fundamentals/external-parameters.md). +The `settings` parameter is an instance of the class. -## Configuration +### Configuration -The .NET Aspire Oracle Entity Framework Core integration provides multiple options to configure the database connection based on the requirements and conventions of your project. +The .NET Aspire Oracle Entity Framework Core integration provides multiple configuration approaches and options to meet 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 can provide the name of the connection string when calling `builder.AddOracleDatabaseDbContext()`: +When using a connection string from the `ConnectionStrings` configuration section, you provide the name of the connection string when calling `builder.AddOracleDatabaseDbContext()`: ```csharp -builder.AddOracleDatabaseDbContext("myConnection"); +builder.AddOracleDatabaseDbContext("oracleConnection"); ``` -And then the connection string will be retrieved from the `ConnectionStrings` configuration section: +The connection string is retrieved from the `ConnectionStrings` configuration section: ```json { "ConnectionStrings": { - "myConnection": "Data Source=TORCL;User Id=myUsername;Password=myPassword;" + "oracleConnection": "Data Source=TORCL;User Id=OracleUser;Password=Non-default-P@ssw0rd;" } } ``` @@ -128,11 +227,11 @@ The `EnrichOracleDatabaseDbContext` won't make use of the `ConnectionStrings` co For more information, see the [ODP.NET documentation](https://www.oracle.com/database/technologies/appdev/dotnet/odp.html). -### Use configuration providers +#### Use configuration providers -The .NET Aspire Oracle Entity Framework Core integration supports [Microsoft.Extensions.Configuration](/dotnet/api/microsoft.extensions.configuration). It loads the `OracleEntityFrameworkCoreSettings` from configuration by using the `Aspire:Oracle:EntityFrameworkCore` key. +The .NET Aspire Oracle Entity Framework Core integration supports from configuration files such as _:::no-loc text="appsettings.json":::_ by using the `Aspire:Oracle:EntityFrameworkCore` key. If you have set up your configurations in the `Aspire:Oracle:EntityFrameworkCore` section you can just call the method without passing any parameter. -The following example shows an _:::no-loc text="appsettings.json":::_ that configures some of the available options: +The following is an example of an _:::no-loc text="appsettings.json":::_ that configures some of the available options: ```json { @@ -141,9 +240,8 @@ The following example shows an _:::no-loc text="appsettings.json":::_ that confi "EntityFrameworkCore": { "DisableHealthChecks": true, "DisableTracing": true, - "DisableMetrics": false, "DisableRetry": false, - "Timeout": 30 + "CommandTimeout": 30 } } } @@ -151,14 +249,14 @@ The following example shows an _:::no-loc text="appsettings.json":::_ that confi ``` > [!TIP] -> The `Timeout` property is in seconds. When set as shown in the preceding example, the timeout is 30 seconds. +> The `CommandTimeout` property is in seconds. When set as shown in the preceding example, the timeout is 30 seconds. -### Use inline delegates +#### 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: +You can also pass the `Action` delegate to set up some or all the options inline, for example to disable health checks from code: ```csharp -builder.AddOracleDatabaseDbContext( +builder.AddOracleDatabaseDbContext( "oracle", static settings => settings.DisableHealthChecks = true); ``` @@ -166,32 +264,53 @@ builder.AddOracleDatabaseDbContext( or ```csharp -builder.EnrichOracleDatabaseDbContext( +builder.EnrichOracleDatabaseDbContext( static settings => settings.DisableHealthChecks = true); ``` +#### Configuration options + +Here are the configurable options with corresponding default values: + +| Name | Description | +|-----------------------|--------------------------------------------------------------------------------------| +| `ConnectionString` | The connection string of the Oracle 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. | +| `DisableRetry` | A boolean value that indicates whether command retries should be disabled or not. | +| `CommandTimeout` | The time in seconds to wait for the command to execute. | + [!INCLUDE [integration-health-checks](../includes/integration-health-checks.md)] -The .NET Aspire Oracle Entity Framework Core integration registers a basic health check that checks the database connection given a `TContext`. The health check is enabled by default and can be disabled using the `DisableHealthChecks` property in the configuration. +By default, the .NET Aspire Oracle Entity Framework Core integration handles the following: + +- Checks if the is `true`. +- If so, adds the [`DbContextHealthCheck`](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/blob/master/src/HealthChecks.NpgSql/NpgSqlHealthCheck.cs), which calls EF Core's method. The name of the health check is the name of the `TContext` type. [!INCLUDE [integration-observability-and-telemetry](../includes/integration-observability-and-telemetry.md)] -### Logging +#### Logging The .NET Aspire Oracle Entity Framework Core integration uses the following log categories: -- `Microsoft.EntityFrameworkCore.Database.Command.CommandCreated` -- `Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting` -- `Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted` -- `Microsoft.EntityFrameworkCore.Database.Command.CommandError` +- `Microsoft.EntityFrameworkCore.ChangeTracking` +- `Microsoft.EntityFrameworkCore.Database.Command` +- `Microsoft.EntityFrameworkCore.Database.Connection` +- `Microsoft.EntityFrameworkCore.Database.Transaction` +- `Microsoft.EntityFrameworkCore.Infrastructure` +- `Microsoft.EntityFrameworkCore.Migrations` +- `Microsoft.EntityFrameworkCore.Model` +- `Microsoft.EntityFrameworkCore.Model.Validation` +- `Microsoft.EntityFrameworkCore.Query` +- `Microsoft.EntityFrameworkCore.Update` -### Tracing +#### Tracing The .NET Aspire Oracle Entity Framework Core integration will emit the following tracing activities using OpenTelemetry: - OpenTelemetry.Instrumentation.EntityFrameworkCore -### Metrics +#### Metrics The .NET Aspire Oracle Entity Framework Core integration currently supports the following metrics: @@ -199,6 +318,8 @@ The .NET Aspire Oracle Entity Framework Core integration currently supports the ## See also +- [Oracle Database](https://www.oracle.com/database/) +- [Oracle Database Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/index.html) - [Entity Framework Core docs](/ef/core) - [.NET Aspire integrations](../fundamentals/integrations-overview.md) - [.NET Aspire GitHub repo](https://github.com/dotnet/aspire)