From 3aa95927a4c889a1f3e7ea3ea81959dc507573b0 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Wed, 12 Feb 2025 17:47:30 +0000 Subject: [PATCH 01/21] Initial commit. --- docs/database/use-entity-framework.md | 97 +++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/database/use-entity-framework.md diff --git a/docs/database/use-entity-framework.md b/docs/database/use-entity-framework.md new file mode 100644 index 0000000000..61ed38e52c --- /dev/null +++ b/docs/database/use-entity-framework.md @@ -0,0 +1,97 @@ +--- +title: Use Entity Framework with .NET Aspire +description: Learn how to use the Entity Framework integrations to interact with a database from code in a .NET Aspire solution. +ms.date: 02/12/2025 +uid: database/use-entity-framework +--- + +# Use Entity Framework with .NET Aspire + +In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework (EF) Object-Relational Mapping (ORM) framework to streamline to process further. + +Developers use ORM frameworks to work with databases using code objects instead of SQL queries. EF automatically codes database interactions by generating SQL queries based on LINQ queries. EF supports various database providers, including SQL Server, PostgreSQL, and MySQL so it's easy to interact with relational databases while following object-oriented principles. + +.NET Aspire includes the following client integrations, which implement EF for different database systems: + +- SQL Server Entity Framework Core integration +- PostgreSQL Entity Framework Core integration +- MySQL Pomelo Entity Framework Core integration +- Oracle Entity Framework Core integration +- Cosmos DB Entity Framework Core integration + +> [!NOTE] +> In .NET Aspire, EF is implemented by client integrations, not hosting integrations. So, for example, to use EF with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. + +## Install the client integration + +> Either repeat this here or link to the individual articles + +## Use a .NET Aspire database context + +In EF, a [database context](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the `DbContext` class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. + + +What does the aspire AddXDbContext method do? + +Why is this good? + +Link to individual articles + +## Enrich an Entity Framework database context + +Use the standard EF way to create a context and add it to the DI container + +Use the Enrich method to add Aspire stuff to it + +Why do it this way? + +Example with interceptors + +## Use Entity Framework context factories in .NET Aspire + +Is it possible? + +Is it good? + +Code + +## Use Entity Framework context pooling in .NET Aspire + +Should this section be part of the previous one? + +Is it possible? + +Is it good? + +Code + +## Entity Framework approaches + +Model first, database-first, code-first + +Migrations are for the last one only. + +## Use Entity Framework migrations + +Intro - what is a migration? How does it work outside of Aspire + +Link to EF documentation + +### Create a migrations service + +Is this the best way to do it? Refer to the migration tutorial + +### Migrations and transactions + +Should you use these or not (I think it depends on the version of Aspire and EF that you're using). + +### Seed the database + +Refer to the existing article + +## See also + +- [Entity Framework documentation hub](/ef) +- [Tutorial: Connect an ASP.NET Core app to SQL Server using .NET Aspire and Entity Framework Core](/dotnet/aspire/database/sql-server-integrations) +- [Apply Entity Framework Core migrations in .NET Aspire](/dotnet/aspire/database/ef-core-migrations) +- [Seed data in a database using .NET Aspire](/aspire/database/seed-database-data) From 7f5b815a18d7ccdb29f0d69d8e6771169238a851 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Thu, 13 Feb 2025 17:45:00 +0000 Subject: [PATCH 02/21] Discussions of Add and Enrich DBContext methods. --- docs/database/use-entity-framework.md | 57 +++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/docs/database/use-entity-framework.md b/docs/database/use-entity-framework.md index 61ed38e52c..89edee7e4e 100644 --- a/docs/database/use-entity-framework.md +++ b/docs/database/use-entity-framework.md @@ -22,6 +22,11 @@ Developers use ORM frameworks to work with databases using code objects instead > [!NOTE] > In .NET Aspire, EF is implemented by client integrations, not hosting integrations. So, for example, to use EF with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. + +## Stuff in the App Host + +Is there stuff to do in the App Host that only applies to EF? + ## Install the client integration > Either repeat this here or link to the individual articles @@ -30,22 +35,60 @@ Developers use ORM frameworks to work with databases using code objects instead In EF, a [database context](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the `DbContext` class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. +The EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server client extension, the method is named and for the PostgreSQL client extension, the method is named . + +These .NET Aspire add context methods: -What does the aspire AddXDbContext method do? +- Check that a database context of the same type is not already registered in the Dependency Injection (DI) container. +- Use the connection name you pass to the method to get the connection string from the application builder. This connection name must match the name used when adding the corresponding resource to the app host project. +- Apply an EF settings object, if you passed one. +- Add a database context pool to the DI container. +- Configure instrumentation, such as tracing and health checks. -Why is this good? +Use these .NET Aspire add context methods when you want a simple way to create the database context and don't need to implement advanced EF techniques. -Link to individual articles +> AJMTODO: Link to individual articles? ## Enrich an Entity Framework database context -Use the standard EF way to create a context and add it to the DI container +Alternatively, you create a database context and add it to the DI container using the standard entity `AddDbContext` method, as used in non-.NET Aspire projects: + +> AJMTODO: pivots here? + +```csharp +builder.Services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); +``` + +You have more flexibility when you create the database context in this way, for example: + +- You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. +- You can use Entity Framework Core interceptors to modify database operations. +- You can choose not to use Entity Framework Core context pooling, which may perform better in some circumstances. + + +By default, a database context create this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: + +- Apply an EF settings object, if you passed one. +- Configure connection retry settings. +- Configure instrumentation, such as tracing and health checks. + +> [!NOTE] +> You must have already added a database context to the DI container before you call an enrich method. -Use the Enrich method to add Aspire stuff to it +> AJMTODO: pivots here? -Why do it this way? +```csharp +builder.EnrichSqlServerDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` -Example with interceptors +> AJMTODO: Example with interceptors ## Use Entity Framework context factories in .NET Aspire From 7c102eef88d61b02bfcb13d784a3aa450c7c5f0b Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Wed, 19 Feb 2025 17:30:11 +0000 Subject: [PATCH 03/21] More material about DbContexts. --- docs/database/use-entity-framework.md | 106 ++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/docs/database/use-entity-framework.md b/docs/database/use-entity-framework.md index 89edee7e4e..0ed6566456 100644 --- a/docs/database/use-entity-framework.md +++ b/docs/database/use-entity-framework.md @@ -22,7 +22,6 @@ Developers use ORM frameworks to work with databases using code objects instead > [!NOTE] > In .NET Aspire, EF is implemented by client integrations, not hosting integrations. So, for example, to use EF with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. - ## Stuff in the App Host Is there stuff to do in the App Host that only applies to EF? @@ -33,9 +32,9 @@ Is there stuff to do in the App Host that only applies to EF? ## Use a .NET Aspire database context -In EF, a [database context](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the `DbContext` class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. +In EF, a [database context](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. -The EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server client extension, the method is named and for the PostgreSQL client extension, the method is named . +The .NET Aspire EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server client extension, the method is named and for the PostgreSQL client extension, the method is named . These .NET Aspire add context methods: @@ -47,11 +46,27 @@ These .NET Aspire add context methods: Use these .NET Aspire add context methods when you want a simple way to create the database context and don't need to implement advanced EF techniques. +> AJMTODO: Pivots? + +```csharp +builder.AddSqlServerDbContext(connectionName: "database"); +``` + +To retrieve the `ExampleDbContext` object from the DI container: + +```csharp +public class ExampleService(ExampleDbContext context) +{ + // Use context... +} +``` + + > AJMTODO: Link to individual articles? ## Enrich an Entity Framework database context -Alternatively, you create a database context and add it to the DI container using the standard entity `AddDbContext` method, as used in non-.NET Aspire projects: +Alternatively, you can create a database context and add it to the DI container using the standard entity framework method, as used in non-.NET Aspire projects: > AJMTODO: pivots here? @@ -64,11 +79,18 @@ builder.Services.AddDbContext(options => You have more flexibility when you create the database context in this way, for example: - You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. -- You can use Entity Framework Core interceptors to modify database operations. - You can choose not to use Entity Framework Core context pooling, which may perform better in some circumstances. +- You can use [Entity Framework Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. + ```csharp + builder.Services.AddDbContext(options => + { + options.UseSqlServer(builder.Configuration.GetConnectionString("database")); + options.AddInterceptors(new ExampleInterceptor()); + }); + ``` -By default, a database context create this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: +By default, a database context created this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: - Apply an EF settings object, if you passed one. - Configure connection retry settings. @@ -88,25 +110,78 @@ builder.EnrichSqlServerDbContext( }); ``` -> AJMTODO: Example with interceptors +Retrieve the database context from the DI container using the same code as the previous example: + +```csharp +public class ExampleService(ExampleDbContext context) +{ + // Use context... +} +``` ## Use Entity Framework context factories in .NET Aspire -Is it possible? +An Entity Framework database context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the Customers table and a row in the Addresses table. You should create the EF context, add the new customer and address entities to it, call or , and then dispose the context. + +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard Entity Framework method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the `Dispose` method when the unit of work is complete. + +> AJMTODO: Review the method names and do xrefs for them. + +Other types of application, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, use a you can create a factory for database contexts, by calling . This method partners well with the .NET Aspire `Enrich\DbContext` methods: + +> AJMTODO: Pivot? + +```csharp +builder.Services.AddDbContextFactory(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichSqlServerDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` -Is it good? +Notice that the above code adds and enriches a database context factory in the DI container. When you retreive this from the container, you must add a line of code to create a database context from it: -Code +```csharp +public class ExampleService(IDbContextFactory contextFactory) +{ + using (var context = contextFactory.CreateDbContext()) + { + // Use context... + } +} +``` + +Database contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. ## Use Entity Framework context pooling in .NET Aspire -Should this section be part of the previous one? +In Entity Framework a database context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a database context pool. + +Database context pooling is a feature of Entity Framework. Database contexts are created as normal but, when you dispose of one, it isn't destroyed but reset and stored in a pool. The next time your code creates a context, the stored one is returned to avoid the extra overhead of creating a new one. + +In a .NET Aspire consuming project, there are three ways to use context pooling: + +- If you use the .NET Aspire `Add\DbContext` methods, a context pool is automatically used. +- Call the method instead of the Entity Framework method. -Is it possible? + ```csharp + builder.Services.AddDbContextPool(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -Is it good? +- Call the method instead of the Entity Framework method. -Code + ```csharp + builder.Services.AddDbPooledContextFactory(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` ## Entity Framework approaches @@ -138,3 +213,6 @@ Refer to the existing article - [Tutorial: Connect an ASP.NET Core app to SQL Server using .NET Aspire and Entity Framework Core](/dotnet/aspire/database/sql-server-integrations) - [Apply Entity Framework Core migrations in .NET Aspire](/dotnet/aspire/database/ef-core-migrations) - [Seed data in a database using .NET Aspire](/aspire/database/seed-database-data) +- [DbContext Lifetime, Configuration, and Initialization](/ef/core/dbcontext-configuration/) +- [Advanced Performance Topics](/ef/core/performance/advanced-performance-topics) +- [Entity Framework Interceptors](/ef/core/logging-events-diagnostics/interceptors) From 0b96baa9df2d2609ee4453b94c44ff18bda7d302 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Thu, 20 Feb 2025 17:36:50 +0000 Subject: [PATCH 04/21] More performance additions. --- docs/database/use-entity-framework.md | 30 +++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/database/use-entity-framework.md b/docs/database/use-entity-framework.md index 0ed6566456..2b9a09a9eb 100644 --- a/docs/database/use-entity-framework.md +++ b/docs/database/use-entity-framework.md @@ -5,6 +5,8 @@ ms.date: 02/12/2025 uid: database/use-entity-framework --- +> AJMTODO: Is this title right? How about "Advanced EF usage..." or something? + # Use Entity Framework with .NET Aspire In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework (EF) Object-Relational Mapping (ORM) framework to streamline to process further. @@ -32,6 +34,8 @@ Is there stuff to do in the App Host that only applies to EF? ## Use a .NET Aspire database context +> AJMTODO: Is this title correct? + In EF, a [database context](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. The .NET Aspire EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server client extension, the method is named and for the PostgreSQL client extension, the method is named . @@ -80,6 +84,10 @@ You have more flexibility when you create the database context in this way, for - You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. - You can choose not to use Entity Framework Core context pooling, which may perform better in some circumstances. +- You can use dynamic connection strings. + + > AJMTODO: Can you? Add an example. Base it on this: https://gavilan.blog/2020/07/09/configuring-entity-framework-core-with-dynamic-connection-strings-asp-net-core/ + - You can use [Entity Framework Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. ```csharp @@ -166,7 +174,7 @@ Database context pooling is a feature of Entity Framework. Database contexts are In a .NET Aspire consuming project, there are three ways to use context pooling: -- If you use the .NET Aspire `Add\DbContext` methods, a context pool is automatically used. +- Use the .NET Aspire `Add\DbContext` methods. These methods create a context pool automatically. - Call the method instead of the Entity Framework method. ```csharp @@ -183,22 +191,36 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` +Remember to enrich the database context after using the last two methods, as described above. + ## Entity Framework approaches Model first, database-first, code-first Migrations are for the last one only. -## Use Entity Framework migrations -Intro - what is a migration? How does it work outside of Aspire +## External databases + +Databases that are not running in Aspire-managed containers. + +## Manage schemas in .NET Aspire projects with Entity Framework migrations + +In a cloud-native solution, such as those .NET Aspire is designed to create, each microservice can use a separate database. As you evolve your microservice and add features, you must make matching changes to the database schema. For example, in your **Customers** microservice, if you build a feature to enable users to store the credit card details, you'll need new columns in your database tables. In Entity Framework, instead of manually implementing schema changes with Data Definition Language (DDL) queries, you can just add properties to the `Customer` class. Create an EF **migration** to propagate those properties to the database. Migrations can add, modify, or delete tables, columns, or relationships in the database schema. Because each migration is a generated code file, it can be included seamlessly in version control with the rest of your code. + +> [!NOTE] +> This article discusses how to use EF migrations in .NET Aspire. For more information about migrations, see [Migrations Overview](/ef/core/managing-schemas/migrations) + +How to use 'em in Aspire? -Link to EF documentation ### Create a migrations service Is this the best way to do it? Refer to the migration tutorial +> AJMTODO: Use the DbContent.Database.Migrate() method? This article says don't: https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli + + ### Migrations and transactions Should you use these or not (I think it depends on the version of Aspire and EF that you're using). From 5a175671cf62de771cd31b8073e27810411f6ae4 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Fri, 21 Feb 2025 16:58:22 +0000 Subject: [PATCH 05/21] Article retitled and more examples added. --- ...md => use-entity-framework-db-contexts.md} | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) rename docs/database/{use-entity-framework.md => use-entity-framework-db-contexts.md} (59%) diff --git a/docs/database/use-entity-framework.md b/docs/database/use-entity-framework-db-contexts.md similarity index 59% rename from docs/database/use-entity-framework.md rename to docs/database/use-entity-framework-db-contexts.md index 2b9a09a9eb..b9cfbf6516 100644 --- a/docs/database/use-entity-framework.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -1,15 +1,13 @@ --- -title: Use Entity Framework with .NET Aspire -description: Learn how to use the Entity Framework integrations to interact with a database from code in a .NET Aspire solution. -ms.date: 02/12/2025 -uid: database/use-entity-framework +title: Use Entity Framework database contexts with .NET Aspire +description: Learn how to optimize the performance of .NET Aspire Entity Framework integrations using their database context objects. +ms.date: 02/21/2025 +uid: database/use-entity-framework-db-contexts --- -> AJMTODO: Is this title right? How about "Advanced EF usage..." or something? +# Use Entity Framework database contexts with .NET Aspire -# Use Entity Framework with .NET Aspire - -In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework (EF) Object-Relational Mapping (ORM) framework to streamline to process further. +In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework (EF) Object-Relational Mapping (ORM) framework to streamline the process further. Developers use ORM frameworks to work with databases using code objects instead of SQL queries. EF automatically codes database interactions by generating SQL queries based on LINQ queries. EF supports various database providers, including SQL Server, PostgreSQL, and MySQL so it's easy to interact with relational databases while following object-oriented principles. @@ -24,21 +22,11 @@ Developers use ORM frameworks to work with databases using code objects instead > [!NOTE] > In .NET Aspire, EF is implemented by client integrations, not hosting integrations. So, for example, to use EF with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. -## Stuff in the App Host - -Is there stuff to do in the App Host that only applies to EF? - -## Install the client integration - -> Either repeat this here or link to the individual articles - -## Use a .NET Aspire database context +## Use .NET Aspire to create a database context -> AJMTODO: Is this title correct? +In EF, a [**database context**](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. -In EF, a [database context](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. - -The .NET Aspire EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server client extension, the method is named and for the PostgreSQL client extension, the method is named . +The .NET Aspire EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server EF client integration, the method is named and for the PostgreSQL client integration, the method is named . These .NET Aspire add context methods: @@ -46,7 +34,7 @@ These .NET Aspire add context methods: - Use the connection name you pass to the method to get the connection string from the application builder. This connection name must match the name used when adding the corresponding resource to the app host project. - Apply an EF settings object, if you passed one. - Add a database context pool to the DI container. -- Configure instrumentation, such as tracing and health checks. +- Configure instrumentation on the database context, such as tracing and health checks. Use these .NET Aspire add context methods when you want a simple way to create the database context and don't need to implement advanced EF techniques. @@ -65,12 +53,11 @@ public class ExampleService(ExampleDbContext context) } ``` - > AJMTODO: Link to individual articles? -## Enrich an Entity Framework database context +## Use EF to create a database context and then enrich it -Alternatively, you can create a database context and add it to the DI container using the standard entity framework method, as used in non-.NET Aspire projects: +Alternatively, you can create a database context and add it to the DI container using the standard EF method, as commonly used in non-.NET Aspire projects: > AJMTODO: pivots here? @@ -83,20 +70,9 @@ builder.Services.AddDbContext(options => You have more flexibility when you create the database context in this way, for example: - You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. -- You can choose not to use Entity Framework Core context pooling, which may perform better in some circumstances. -- You can use dynamic connection strings. - - > AJMTODO: Can you? Add an example. Base it on this: https://gavilan.blog/2020/07/09/configuring-entity-framework-core-with-dynamic-connection-strings-asp-net-core/ - -- You can use [Entity Framework Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. - - ```csharp - builder.Services.AddDbContext(options => - { - options.UseSqlServer(builder.Configuration.GetConnectionString("database")); - options.AddInterceptors(new ExampleInterceptor()); - }); - ``` +- You can choose not to use EF Core context pooling, which may perform better in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) +- You can use dynamic connection strings. For more information, see [Use EF with dynamic connection strings in .NET Aspire](#use-ef-with-dynamic-connection-strings-in-net-aspire) +- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-inteceptors-in-net-aspire) By default, a database context created this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: @@ -105,7 +81,7 @@ By default, a database context created this way doesn't include .NET Aspire feat - Configure instrumentation, such as tracing and health checks. > [!NOTE] -> You must have already added a database context to the DI container before you call an enrich method. +> You must have added a database context to the DI container before you call an enrich method. > AJMTODO: pivots here? @@ -127,15 +103,35 @@ public class ExampleService(ExampleDbContext context) } ``` -## Use Entity Framework context factories in .NET Aspire +### Use EF with dynamic connection strings in .NET Aspire + + > AJMTODO: Can you? Add an example. Base it on this: https://gavilan.blog/2020/07/09/configuring-entity-framework-core-with-dynamic-connection-strings-asp-net-core/ + +### Use EF interceptors in .NET Aspire + +> AJMTODO: intro them here + +> AJMTODO: Pivots + + ```csharp + builder.Services.AddDbContext(options => + { + options.UseSqlServer(builder.Configuration.GetConnectionString("database")); + options.AddInterceptors(new ExampleInterceptor()); + }); + ``` + +> AJMTODO: enrich method + +### Use EF Core context factories in .NET Aspire -An Entity Framework database context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the Customers table and a row in the Addresses table. You should create the EF context, add the new customer and address entities to it, call or , and then dispose the context. +An EF database context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should create the EF context, add the new customer and address entities to it, call or , and then dispose the context. -In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard Entity Framework method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the `Dispose` method when the unit of work is complete. +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the `Dispose` method when the unit of work is complete. > AJMTODO: Review the method names and do xrefs for them. -Other types of application, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, use a you can create a factory for database contexts, by calling . This method partners well with the .NET Aspire `Enrich\DbContext` methods: +Other types of application, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, use a you can create a factory for database contexts, by calling the EF method. This method partners well with the .NET Aspire `Enrich\DbContext` methods: > AJMTODO: Pivot? @@ -152,7 +148,7 @@ builder.EnrichSqlServerDbContext( }); ``` -Notice that the above code adds and enriches a database context factory in the DI container. When you retreive this from the container, you must add a line of code to create a database context from it: +Notice that the above code adds and enriches a *database context factory* in the DI container. When you retreive this from the container, you must add a line of code to create a *database context* from it: ```csharp public class ExampleService(IDbContextFactory contextFactory) @@ -166,16 +162,16 @@ public class ExampleService(IDbContextFactory contextFactory) Database contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. -## Use Entity Framework context pooling in .NET Aspire +## Use EF context pooling in .NET Aspire -In Entity Framework a database context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a database context pool. +In EF Core a database context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a database context pool. -Database context pooling is a feature of Entity Framework. Database contexts are created as normal but, when you dispose of one, it isn't destroyed but reset and stored in a pool. The next time your code creates a context, the stored one is returned to avoid the extra overhead of creating a new one. +Database context pooling is a feature of EF Core. Database contexts are created as normal but, when you dispose of one, it isn't destroyed but reset and stored in a pool. The next time your code creates a context, the stored one is returned to avoid the extra overhead of creating a new one. In a .NET Aspire consuming project, there are three ways to use context pooling: -- Use the .NET Aspire `Add\DbContext` methods. These methods create a context pool automatically. -- Call the method instead of the Entity Framework method. +- Use the .NET Aspire `Add\DbContext` methods to create the database context. These methods create a context pool automatically. +- Call the EF method instead of the EF method. ```csharp builder.Services.AddDbContextPool(options => @@ -183,7 +179,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` -- Call the method instead of the Entity Framework method. +- Call the EF method instead of the EF method. ```csharp builder.Services.AddDbPooledContextFactory(options => @@ -193,7 +189,11 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: Remember to enrich the database context after using the last two methods, as described above. -## Entity Framework approaches + + +> AJMTODO: The rest of this must be moved. + +## EF approaches Model first, database-first, code-first @@ -204,9 +204,9 @@ Migrations are for the last one only. Databases that are not running in Aspire-managed containers. -## Manage schemas in .NET Aspire projects with Entity Framework migrations +## Manage schemas in .NET Aspire projects with EF migrations -In a cloud-native solution, such as those .NET Aspire is designed to create, each microservice can use a separate database. As you evolve your microservice and add features, you must make matching changes to the database schema. For example, in your **Customers** microservice, if you build a feature to enable users to store the credit card details, you'll need new columns in your database tables. In Entity Framework, instead of manually implementing schema changes with Data Definition Language (DDL) queries, you can just add properties to the `Customer` class. Create an EF **migration** to propagate those properties to the database. Migrations can add, modify, or delete tables, columns, or relationships in the database schema. Because each migration is a generated code file, it can be included seamlessly in version control with the rest of your code. +In a cloud-native solution, such as those .NET Aspire is designed to create, each microservice can use a separate database. As you evolve your microservice and add features, you must make matching changes to the database schema. For example, in your **Customers** microservice, if you build a feature to enable users to store the credit card details, you'll need new columns in your database tables. In EF, instead of manually implementing schema changes with Data Definition Language (DDL) queries, you can just add properties to the `Customer` class. Create an EF **migration** to propagate those properties to the database. Migrations can add, modify, or delete tables, columns, or relationships in the database schema. Because each migration is a generated code file, it can be included seamlessly in version control with the rest of your code. > [!NOTE] > This article discusses how to use EF migrations in .NET Aspire. For more information about migrations, see [Migrations Overview](/ef/core/managing-schemas/migrations) From 78c7d007ea55040e6802b4e8f31208308aabe74f Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Mon, 24 Feb 2025 16:45:33 +0000 Subject: [PATCH 06/21] New article on EF DB contexts first draft complete. --- .../use-entity-framework-db-contexts.md | 432 +++++++++++++++--- docs/zones/zone-pivot-groups.yml | 12 + 2 files changed, 390 insertions(+), 54 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index b9cfbf6516..5a6598f45c 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -1,8 +1,8 @@ --- -title: Use Entity Framework database contexts with .NET Aspire description: Learn how to optimize the performance of .NET Aspire Entity Framework integrations using their database context objects. ms.date: 02/21/2025 uid: database/use-entity-framework-db-contexts +zone_pivot_groups: entity-framework-client-integration --- # Use Entity Framework database contexts with .NET Aspire @@ -38,13 +38,48 @@ These .NET Aspire add context methods: Use these .NET Aspire add context methods when you want a simple way to create the database context and don't need to implement advanced EF techniques. -> AJMTODO: Pivots? +:::zone pivot="sql-server-ef" ```csharp builder.AddSqlServerDbContext(connectionName: "database"); ``` -To retrieve the `ExampleDbContext` object from the DI container: +> [!TIP] +> For full information about SQL Server hosting and client integrations, see [.NET Aspire SQL Server Entity Framework Core integration](/dotnet/aspire/database/sql-server-entity-framework-integration). + +:::zone-end +:::zone pivot="postgresql-ef" + +```csharp +builder.AddNpgsqlDbContext(connectionName: "database"); +``` + +> [!TIP] +> For full information about PostgreSQL hosting and client integrations, see [.NET Aspire PostgreSQL Entity Framework Core integration](/dotnet/aspire/database/postgresql-entity-framework-integration). + +:::zone-end +:::zone pivot="oracle-ef" + +```csharp +builder.AddOracleDatabaseDbContext(connectionName: "database"); +``` + +> [!TIP] +> For full information about Oracle Database hosting and client integrations, see [.NET Aspire Oracle Entity Framework Core integration](/dotnet/aspire/database/oracle-entity-framework-integration). + +:::zone-end +:::zone pivot="mysql-ef" + +```csharp +builder.AddMySqlDbContext(connectionName: "database"); +``` + +> [!TIP] +> For full information about MySQL hosting and client integrations, see [.NET Aspire Pomelo MySQL Entity Framework Core integration](/dotnet/aspire/database/mysql-entity-framework-integration). + +:::zone-end + +To retrieve the `ExampleDbContext` object from the DI container and use the database: ```csharp public class ExampleService(ExampleDbContext context) @@ -53,13 +88,11 @@ public class ExampleService(ExampleDbContext context) } ``` -> AJMTODO: Link to individual articles? - ## Use EF to create a database context and then enrich it Alternatively, you can create a database context and add it to the DI container using the standard EF method, as commonly used in non-.NET Aspire projects: -> AJMTODO: pivots here? +:::zone pivot="sql-server-ef" ```csharp builder.Services.AddDbContext(options => @@ -67,6 +100,35 @@ builder.Services.AddDbContext(options => ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` +:::zone-end +:::zone pivot="postgresql-ef" + +```csharp +builder.Services.AddDbContext(options => + options.UseNpgsql(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); +``` + +:::zone-end +:::zone pivot="oracle-ef" + +```csharp +builder.Services.AddDbContext(options => + options.UseOracle(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); +``` + +:::zone-end +:::zone pivot="sql-server-ef" + +```csharp +builder.Services.AddDbContext(options => + options.UseMySql(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); +``` + +:::zone-end + You have more flexibility when you create the database context in this way, for example: - You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. @@ -83,7 +145,7 @@ By default, a database context created this way doesn't include .NET Aspire feat > [!NOTE] > You must have added a database context to the DI container before you call an enrich method. -> AJMTODO: pivots here? +:::zone pivot="sql-server-ef" ```csharp builder.EnrichSqlServerDbContext( @@ -94,6 +156,44 @@ builder.EnrichSqlServerDbContext( }); ``` +:::zone-end +:::zone pivot="postgresql-ef" + +```csharp +builder.EnrichNpgsqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="oracle-ef" + +```csharp +builder.EnrichOracleDatabaseDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="mysql-ef" + +```csharp +builder.EnrichMySqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end + Retrieve the database context from the DI container using the same code as the previous example: ```csharp @@ -103,37 +203,189 @@ public class ExampleService(ExampleDbContext context) } ``` +### Use EF interceptors in .NET Aspire + +EF interceptors allow developers to hook into and modify database operations at various points during the execution of database queries and commands. You can use them to log, modify, or suppress operations with your own code. Your interceptor must implement one or more interface from the interface. + +Again, interceptors are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: + +:::zone-pivot="sql-server-ef" + +```csharp +builder.Services.AddDbContext(options => + { + options.UseSqlServer(builder.Configuration.GetConnectionString("database")); + options.AddInterceptors(new ExampleInterceptor()); + }); + +builder.EnrichSqlServerDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone-pivot="postgresql-ef" + +```csharp +builder.Services.AddDbContext(options => + { + options.UseNpgsql(builder.Configuration.GetConnectionString("database")); + options.AddInterceptors(new ExampleInterceptor()); + }); + +builder.EnrichNpgsqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone-pivot="oracle-ef" + +```csharp +builder.Services.AddDbContext(options => + { + options.UseOracle(builder.Configuration.GetConnectionString("database")); + options.AddInterceptors(new ExampleInterceptor()); + }); + +builder.EnrichOracleDatabaseDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone-pivot="mysql-ef" + +```csharp +builder.Services.AddDbContext(options => + { + options.UseMySql(builder.Configuration.GetConnectionString("database")); + options.AddInterceptors(new ExampleInterceptor()); + }); + +builder.EnrichMySqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end + +> [!NOTE] +> For more information about EF interceptors and their use, see [Interceptors](/ef/core/logging-events-diagnostics/interceptors). + ### Use EF with dynamic connection strings in .NET Aspire - > AJMTODO: Can you? Add an example. Base it on this: https://gavilan.blog/2020/07/09/configuring-entity-framework-core-with-dynamic-connection-strings-asp-net-core/ +Most microservices always connect to the same database with the same credentials and other settings, so they always use the same connection string unless there's a major change in infrastructure. However, you may need to change the connection string for each request. For example: -### Use EF interceptors in .NET Aspire +- You might offer your service to multiple tenants and need to use a different database depending on which customer made the request. +- You might need to authenticate the request with a different database user account depending on which customer made the request. -> AJMTODO: intro them here +For these requirements, you can use code to formulate a **dynamic connection strings** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF method to create the database context and then enrich it: -> AJMTODO: Pivots +:::zone pivot="sql-server-ef" - ```csharp - builder.Services.AddDbContext(options => - { - options.UseSqlServer(builder.Configuration.GetConnectionString("database")); - options.AddInterceptors(new ExampleInterceptor()); - }); - ``` - -> AJMTODO: enrich method +```csharp +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); + +connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); + +builder.Services.AddDbContext(options => + options.UseSqlServer(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichSqlServerDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="postgresql-ef" + +```csharp +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); + +connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); + +builder.Services.AddDbContext(options => + options.UseNpgsql(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichNpgsqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="oracle-ef" + +```csharp +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); + +connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); + +builder.Services.AddDbContext(options => + options.UseOracle(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichOracleDatabaseDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="mysql-ef" + +```csharp +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); + +connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); + +builder.Services.AddDbContext(options => + options.UseMySql(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichMySqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end + +The above code replaces the place holder `{DatabaseName}` in the connection string with the string `ContosoDatabase`, at run time, before it creates the database context and enriches it. ### Use EF Core context factories in .NET Aspire An EF database context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should create the EF context, add the new customer and address entities to it, call or , and then dispose the context. -In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the `Dispose` method when the unit of work is complete. - -> AJMTODO: Review the method names and do xrefs for them. +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. Other types of application, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, use a you can create a factory for database contexts, by calling the EF method. This method partners well with the .NET Aspire `Enrich\DbContext` methods: -> AJMTODO: Pivot? +:::zone pivot="sql-server-ef" ```csharp builder.Services.AddDbContextFactory(options => @@ -148,6 +400,56 @@ builder.EnrichSqlServerDbContext( }); ``` +:::zone-end +:::zone pivot="postgresql-ef" + +```csharp +builder.Services.AddDbContextFactory(options => + options.UseNpgsql(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichNpgsqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="oracle-ef" + +```csharp +builder.Services.AddDbContextFactory(options => + options.UseOracle(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichOracleDatabaseDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end +:::zone pivot="mysql-ef" + +```csharp +builder.Services.AddDbContextFactory(options => + options.UseMySql(connectionString + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + +builder.EnrichMySqlDbContext( + configureSettings: settings => + { + settings.DisableRetry = false; + settings.CommandTimeout = 30 // seconds + }); +``` + +:::zone-end + Notice that the above code adds and enriches a *database context factory* in the DI container. When you retreive this from the container, you must add a line of code to create a *database context* from it: ```csharp @@ -160,7 +462,7 @@ public class ExampleService(IDbContextFactory contextFactory) } ``` -Database contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. +Database contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. ## Use EF context pooling in .NET Aspire @@ -173,61 +475,83 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: - Use the .NET Aspire `Add\DbContext` methods to create the database context. These methods create a context pool automatically. - Call the EF method instead of the EF method. + :::zone pivot="sql-server-ef" + ```csharp builder.Services.AddDbContextPool(options => options.UseSqlServer(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` -- Call the EF method instead of the EF method. + :::zone-end + :::zone pivot="postgresql-ef" ```csharp - builder.Services.AddDbPooledContextFactory(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("database") + builder.Services.AddDbContextPool(options => + options.UseNpgsql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` -Remember to enrich the database context after using the last two methods, as described above. - - + :::zone-end + :::zone pivot="oracle-ef" -> AJMTODO: The rest of this must be moved. - -## EF approaches - -Model first, database-first, code-first - -Migrations are for the last one only. - - -## External databases + ```csharp + builder.Services.AddDbContextPool(options => + options.UseOracle(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -Databases that are not running in Aspire-managed containers. + :::zone-end + :::zone pivot="mysql-ef" -## Manage schemas in .NET Aspire projects with EF migrations + ```csharp + builder.Services.AddDbContextPool(options => + options.UseMySql(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -In a cloud-native solution, such as those .NET Aspire is designed to create, each microservice can use a separate database. As you evolve your microservice and add features, you must make matching changes to the database schema. For example, in your **Customers** microservice, if you build a feature to enable users to store the credit card details, you'll need new columns in your database tables. In EF, instead of manually implementing schema changes with Data Definition Language (DDL) queries, you can just add properties to the `Customer` class. Create an EF **migration** to propagate those properties to the database. Migrations can add, modify, or delete tables, columns, or relationships in the database schema. Because each migration is a generated code file, it can be included seamlessly in version control with the rest of your code. + :::zone-end -> [!NOTE] -> This article discusses how to use EF migrations in .NET Aspire. For more information about migrations, see [Migrations Overview](/ef/core/managing-schemas/migrations) +- Call the EF method instead of the EF method. -How to use 'em in Aspire? + :::zone pivot="sql-server-ef" + ```csharp + builder.Services.AddDbPooledContextFactory(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -### Create a migrations service + :::zone-end + :::zone pivot="postgresql-ef" -Is this the best way to do it? Refer to the migration tutorial + ```csharp + builder.Services.AddDbPooledContextFactory(options => + options.UseNpgsql(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -> AJMTODO: Use the DbContent.Database.Migrate() method? This article says don't: https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli + :::zone-end + :::zone pivot="oracle-ef" + ```csharp + builder.Services.AddDbPooledContextFactory(options => + options.UseOracle(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -### Migrations and transactions + :::zone-end + :::zone pivot="mysql-ef" -Should you use these or not (I think it depends on the version of Aspire and EF that you're using). + ```csharp + builder.Services.AddDbPooledContextFactory(options => + options.UseMySql(builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."))); + ``` -### Seed the database + :::zone-end -Refer to the existing article +Remember to enrich the database context after using the last two methods, as described above. ## See also diff --git a/docs/zones/zone-pivot-groups.yml b/docs/zones/zone-pivot-groups.yml index 5e126b4ff0..37d79da11c 100644 --- a/docs/zones/zone-pivot-groups.yml +++ b/docs/zones/zone-pivot-groups.yml @@ -63,3 +63,15 @@ groups: title: MSTest - id: nunit title: NUnit +- id: entity-framework-client-integration + title: .NET Aspire Entity Framework client integration + prompt: Choose a .NET Aspire Entity Framework client integration + pivots: + - id: sql-server-ef + title: SQL Server Entity Framework Core + - id: postgresql-ef + title: PostgreSQL Entity Framework Core + - id: oracle-ef + title: Oracle Entity Framework Core + - id: mysql-ef + title: Pomelo MySQL Entity Framework Core From 0641ad407b32f8fcc2dec52d63b6b99cdc80f2f9 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Mon, 24 Feb 2025 17:13:37 +0000 Subject: [PATCH 07/21] Fixed links and the title. --- docs/database/use-entity-framework-db-contexts.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index 5a6598f45c..62ba9a6a89 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -1,4 +1,5 @@ --- +title: Use Entity Framework database contexts with .NET Aspire description: Learn how to optimize the performance of .NET Aspire Entity Framework integrations using their database context objects. ms.date: 02/21/2025 uid: database/use-entity-framework-db-contexts @@ -134,7 +135,7 @@ You have more flexibility when you create the database context in this way, for - You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. - You can choose not to use EF Core context pooling, which may perform better in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) - You can use dynamic connection strings. For more information, see [Use EF with dynamic connection strings in .NET Aspire](#use-ef-with-dynamic-connection-strings-in-net-aspire) -- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-inteceptors-in-net-aspire) +- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-interceptors-in-net-aspire) By default, a database context created this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: @@ -207,7 +208,7 @@ public class ExampleService(ExampleDbContext context) EF interceptors allow developers to hook into and modify database operations at various points during the execution of database queries and commands. You can use them to log, modify, or suppress operations with your own code. Your interceptor must implement one or more interface from the interface. -Again, interceptors are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: +Again, interceptors are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: :::zone-pivot="sql-server-ef" @@ -381,7 +382,7 @@ The above code replaces the place holder `{DatabaseName}` in the connection stri An EF database context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should create the EF context, add the new customer and address entities to it, call or , and then dispose the context. -In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. Other types of application, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, use a you can create a factory for database contexts, by calling the EF method. This method partners well with the .NET Aspire `Enrich\DbContext` methods: @@ -473,7 +474,7 @@ Database context pooling is a feature of EF Core. Database contexts are created In a .NET Aspire consuming project, there are three ways to use context pooling: - Use the .NET Aspire `Add\DbContext` methods to create the database context. These methods create a context pool automatically. -- Call the EF method instead of the EF method. +- Call the EF method instead of the EF method. :::zone pivot="sql-server-ef" @@ -512,7 +513,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone-end -- Call the EF method instead of the EF method. +- Call the EF method instead of the EF method. :::zone pivot="sql-server-ef" @@ -558,7 +559,6 @@ Remember to enrich the database context after using the last two methods, as des - [Entity Framework documentation hub](/ef) - [Tutorial: Connect an ASP.NET Core app to SQL Server using .NET Aspire and Entity Framework Core](/dotnet/aspire/database/sql-server-integrations) - [Apply Entity Framework Core migrations in .NET Aspire](/dotnet/aspire/database/ef-core-migrations) -- [Seed data in a database using .NET Aspire](/aspire/database/seed-database-data) - [DbContext Lifetime, Configuration, and Initialization](/ef/core/dbcontext-configuration/) - [Advanced Performance Topics](/ef/core/performance/advanced-performance-topics) - [Entity Framework Interceptors](/ef/core/logging-events-diagnostics/interceptors) From d2db5a26dbc4d9c4b8346f5d1b09a20cc1d2fae9 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Mon, 24 Feb 2025 17:18:44 +0000 Subject: [PATCH 08/21] Corrected one more link. --- docs/database/use-entity-framework-db-contexts.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index 62ba9a6a89..aec63af4d6 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -513,12 +513,12 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone-end -- Call the EF method instead of the EF method. +- Call the EF method instead of the EF method. :::zone pivot="sql-server-ef" ```csharp - builder.Services.AddDbPooledContextFactory(options => + builder.Services.AddPooledDbContextFactory(options => options.UseSqlServer(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` @@ -527,7 +527,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone pivot="postgresql-ef" ```csharp - builder.Services.AddDbPooledContextFactory(options => + builder.Services.AddPooledDbContextFactory(options => options.UseNpgsql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` @@ -536,7 +536,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone pivot="oracle-ef" ```csharp - builder.Services.AddDbPooledContextFactory(options => + builder.Services.AddPooledDbContextFactory(options => options.UseOracle(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` @@ -545,7 +545,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone pivot="mysql-ef" ```csharp - builder.Services.AddDbPooledContextFactory(options => + builder.Services.AddPooledDbContextFactory(options => options.UseMySql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` From f435f69002c8efcff2a68a9588bf5fb0b6da08a4 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Mon, 24 Feb 2025 17:26:41 +0000 Subject: [PATCH 09/21] Added new article to the TOC. --- docs/toc.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/toc.yml b/docs/toc.yml index 60c2dea8a2..022e1eb877 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -312,6 +312,9 @@ items: href: community-toolkit/hosting-data-api-builder.md - name: EventStore href: community-toolkit/hosting-eventstore.md + - name: Use Entity Framework database contexts + href: database/use-entity-framework-db-contexts.md + - name: Aspire.Hosting API reference href: /dotnet/api/?term=Aspire.Hosting&view=dotnet-aspire-9.0&preserve-view=true From 99f2c4074d45efccf63ae9bc193f4bf210ca2d30 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Wed, 26 Feb 2025 15:49:17 +0000 Subject: [PATCH 10/21] Integrated feedback from @AndriySvyryd. --- .../use-entity-framework-db-contexts.md | 110 ++++++++++-------- docs/toc.yml | 2 +- 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index aec63af4d6..6c02951f56 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -1,18 +1,18 @@ --- -title: Use Entity Framework database contexts with .NET Aspire -description: Learn how to optimize the performance of .NET Aspire Entity Framework integrations using their database context objects. +title: Use Entity Framework Core with .NET Aspire +description: Learn how to optimize the performance of .NET Aspire Entity Framework integrations using their context objects. ms.date: 02/21/2025 uid: database/use-entity-framework-db-contexts zone_pivot_groups: entity-framework-client-integration --- -# Use Entity Framework database contexts with .NET Aspire +# Use Entity Framework Core with .NET Aspire In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework (EF) Object-Relational Mapping (ORM) framework to streamline the process further. Developers use ORM frameworks to work with databases using code objects instead of SQL queries. EF automatically codes database interactions by generating SQL queries based on LINQ queries. EF supports various database providers, including SQL Server, PostgreSQL, and MySQL so it's easy to interact with relational databases while following object-oriented principles. -.NET Aspire includes the following client integrations, which implement EF for different database systems: +The most commonly used .NET Aspire EF client integrations are: - SQL Server Entity Framework Core integration - PostgreSQL Entity Framework Core integration @@ -25,7 +25,7 @@ Developers use ORM frameworks to work with databases using code objects instead ## Use .NET Aspire to create a database context -In EF, a [**database context**](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Database contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. +In EF, a [**context**](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. The .NET Aspire EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server EF client integration, the method is named and for the PostgreSQL client integration, the method is named . @@ -33,11 +33,14 @@ These .NET Aspire add context methods: - Check that a database context of the same type is not already registered in the Dependency Injection (DI) container. - Use the connection name you pass to the method to get the connection string from the application builder. This connection name must match the name used when adding the corresponding resource to the app host project. -- Apply an EF settings object, if you passed one. -- Add a database context pool to the DI container. -- Configure instrumentation on the database context, such as tracing and health checks. +- Apply the `DbContext` options, if you passed them. +- Add the specified `DbContext` to the DI container with context pooling enabled. +- Apply the recommended defaults, unless you've disabled them through the Aspire EF settings: + - Enable tracing. + - Enable health checks. + - Enable connection resiliency. -Use these .NET Aspire add context methods when you want a simple way to create the database context and don't need to implement advanced EF techniques. +Use these .NET Aspire add context methods when you want a simple way to create the database context and don't yet need advanced EF customization. :::zone pivot="sql-server-ef" @@ -80,7 +83,7 @@ builder.AddMySqlDbContext(connectionName: "database"); :::zone-end -To retrieve the `ExampleDbContext` object from the DI container and use the database: +Obtain the `ExampleDbContext` object from the DI container in the same way as for any other service: ```csharp public class ExampleService(ExampleDbContext context) @@ -89,14 +92,14 @@ public class ExampleService(ExampleDbContext context) } ``` -## Use EF to create a database context and then enrich it +## Use EF to add a context and then enrich it -Alternatively, you can create a database context and add it to the DI container using the standard EF method, as commonly used in non-.NET Aspire projects: +Alternatively, you can add a context to the DI container using the standard EF method, as commonly used in non-.NET Aspire projects: :::zone pivot="sql-server-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool(options => options.UseSqlServer(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` @@ -105,7 +108,7 @@ builder.Services.AddDbContext(options => :::zone pivot="postgresql-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool(options => options.UseNpgsql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` @@ -114,16 +117,16 @@ builder.Services.AddDbContext(options => :::zone pivot="oracle-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool(options => options.UseOracle(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` :::zone-end -:::zone pivot="sql-server-ef" +:::zone pivot="mysql-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool(options => options.UseMySql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); ``` @@ -132,19 +135,23 @@ builder.Services.AddDbContext(options => You have more flexibility when you create the database context in this way, for example: -- You can reuse existing configuration code for the database context without rewriting it for .NET Aspire. -- You can choose not to use EF Core context pooling, which may perform better in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) +- You can reuse existing configuration code for the context without rewriting it for .NET Aspire. +- You can choose not to use EF Core context pooling, which may be necessary in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) +- You can use EF Core context factories or change the lifetime for the EF services. For more information, see [Use EF Core context factories in .NET Aspire](#use-ef-core-context-factories-in-net-aspire) - You can use dynamic connection strings. For more information, see [Use EF with dynamic connection strings in .NET Aspire](#use-ef-with-dynamic-connection-strings-in-net-aspire) -- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-interceptors-in-net-aspire) +- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) that depend on DI services to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-interceptors-in-net-aspire) -By default, a database context created this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: +By default, a context configured this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: - Apply an EF settings object, if you passed one. - Configure connection retry settings. -- Configure instrumentation, such as tracing and health checks. +- Apply the recommended defaults, unless you've disabled them through the .NET Aspire EF settings: + - Enable tracing. + - Enable health checks. + - Enable connection resiliency. > [!NOTE] -> You must have added a database context to the DI container before you call an enrich method. +> You must add a context to the DI container before you call an enrich method. :::zone pivot="sql-server-ef" @@ -204,19 +211,19 @@ public class ExampleService(ExampleDbContext context) } ``` -### Use EF interceptors in .NET Aspire +### Use EF interceptors with .NET Aspire EF interceptors allow developers to hook into and modify database operations at various points during the execution of database queries and commands. You can use them to log, modify, or suppress operations with your own code. Your interceptor must implement one or more interface from the interface. -Again, interceptors are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: +Interceptors that depend on DI services are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: :::zone-pivot="sql-server-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool((serviceProvider, options) => { options.UseSqlServer(builder.Configuration.GetConnectionString("database")); - options.AddInterceptors(new ExampleInterceptor()); + options.AddInterceptors(serviceProvider.GetRequiredService()); }); builder.EnrichSqlServerDbContext( @@ -231,10 +238,10 @@ builder.EnrichSqlServerDbContext( :::zone-pivot="postgresql-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool((serviceProvider, options) => { options.UseNpgsql(builder.Configuration.GetConnectionString("database")); - options.AddInterceptors(new ExampleInterceptor()); + options.AddInterceptors(serviceProvider.GetRequiredService()); }); builder.EnrichNpgsqlDbContext( @@ -249,10 +256,10 @@ builder.EnrichNpgsqlDbContext( :::zone-pivot="oracle-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool((serviceProvider, options) => { options.UseOracle(builder.Configuration.GetConnectionString("database")); - options.AddInterceptors(new ExampleInterceptor()); + options.AddInterceptors(serviceProvider.GetRequiredService( @@ -267,10 +274,10 @@ builder.EnrichOracleDatabaseDbContext( :::zone-pivot="mysql-ef" ```csharp -builder.Services.AddDbContext(options => +builder.Services.AddDbContextPool((serviceProvider, options) => { options.UseMySql(builder.Configuration.GetConnectionString("database")); - options.AddInterceptors(new ExampleInterceptor()); + options.AddInterceptors(serviceProvider.GetRequiredService()); }); builder.EnrichMySqlDbContext( @@ -293,12 +300,13 @@ Most microservices always connect to the same database with the same credentials - You might offer your service to multiple tenants and need to use a different database depending on which customer made the request. - You might need to authenticate the request with a different database user account depending on which customer made the request. -For these requirements, you can use code to formulate a **dynamic connection strings** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF method to create the database context and then enrich it: +For these requirements, you can use code to formulate a **dynamic connection string** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF method to create the database context and then enrich it: :::zone pivot="sql-server-ef" ```csharp -var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -318,7 +326,8 @@ builder.EnrichSqlServerDbContext( :::zone pivot="postgresql-ef" ```csharp -var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") + throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -338,7 +347,8 @@ builder.EnrichNpgsqlDbContext( :::zone pivot="oracle-ef" ```csharp -var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") + throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -358,7 +368,8 @@ builder.EnrichOracleDatabaseDbContext( :::zone pivot="mysql-ef" ```csharp -var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database"); +var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") + throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -380,16 +391,16 @@ The above code replaces the place holder `{DatabaseName}` in the connection stri ### Use EF Core context factories in .NET Aspire -An EF database context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should create the EF context, add the new customer and address entities to it, call or , and then dispose the context. +An EF context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should get the EF context, add the new customer and address entities to it, call , and then dispose the context. -In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to create a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to register a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. -Other types of application, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, use a you can create a factory for database contexts, by calling the EF method. This method partners well with the .NET Aspire `Enrich\DbContext` methods: +Other application types, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, you can register a context factory, by calling the EF method. This method also partners well with the .NET Aspire `Enrich\DbContext` methods: :::zone pivot="sql-server-ef" ```csharp -builder.Services.AddDbContextFactory(options => +builder.Services.AddPooledDbContextFactory(options => options.UseSqlServer(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); @@ -405,7 +416,7 @@ builder.EnrichSqlServerDbContext( :::zone pivot="postgresql-ef" ```csharp -builder.Services.AddDbContextFactory(options => +builder.Services.AddPooledDbContextFactory(options => options.UseNpgsql(connectionString ?? throw new InvalidOperationException("Connection string 'database' not found."))); @@ -421,7 +432,7 @@ builder.EnrichNpgsqlDbContext( :::zone pivot="oracle-ef" ```csharp -builder.Services.AddDbContextFactory(options => +builder.Services.AddPooledDbContextFactory(options => options.UseOracle(connectionString ?? throw new InvalidOperationException("Connection string 'database' not found."))); @@ -437,7 +448,7 @@ builder.EnrichOracleDatabaseDbContext( :::zone pivot="mysql-ef" ```csharp -builder.Services.AddDbContextFactory(options => +builder.Services.AddPooledDbContextFactory(options => options.UseMySql(connectionString ?? throw new InvalidOperationException("Connection string 'database' not found."))); @@ -451,7 +462,7 @@ builder.EnrichMySqlDbContext( :::zone-end -Notice that the above code adds and enriches a *database context factory* in the DI container. When you retreive this from the container, you must add a line of code to create a *database context* from it: +Notice that the above code adds and enriches a *context factory* in the DI container. When you retreive this from the container, you must add a line of code to create a *context* from it: ```csharp public class ExampleService(IDbContextFactory contextFactory) @@ -463,13 +474,13 @@ public class ExampleService(IDbContextFactory contextFactory) } ``` -Database contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. +Contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. ## Use EF context pooling in .NET Aspire In EF Core a database context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a database context pool. -Database context pooling is a feature of EF Core. Database contexts are created as normal but, when you dispose of one, it isn't destroyed but reset and stored in a pool. The next time your code creates a context, the stored one is returned to avoid the extra overhead of creating a new one. +Context pooling is a feature of EF Core. Contexts are created as normal but, when you dispose of one, it isn't destroyed but reset and stored in a pool. The next time your code creates a context, the stored one is returned to avoid the extra overhead of creating a new one. In a .NET Aspire consuming project, there are three ways to use context pooling: @@ -554,6 +565,9 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: Remember to enrich the database context after using the last two methods, as described above. +> [!IMPORTANT] +> Only the base context state is reset when it's returned to the pool. If you've manually changed the state of the `DbConnection` or another service, you must also manually reset it. Additionally, context pooling prevents you from using `OnConfiguring` to configure the context. See [DbContext pooling](/ef/core/performance/advanced-performance-topics#dbcontext-pooling) for more information. + ## See also - [Entity Framework documentation hub](/ef) diff --git a/docs/toc.yml b/docs/toc.yml index 022e1eb877..da1303e86b 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -312,7 +312,7 @@ items: href: community-toolkit/hosting-data-api-builder.md - name: EventStore href: community-toolkit/hosting-eventstore.md - - name: Use Entity Framework database contexts + - name: Use Entity Framework Core for database interaction href: database/use-entity-framework-db-contexts.md - name: Aspire.Hosting API reference From 916d327499fe48c62ebd4203c708dd634af85d95 Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Wed, 26 Feb 2025 15:53:45 +0000 Subject: [PATCH 11/21] Fixed a section link. --- docs/database/use-entity-framework-db-contexts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index 6c02951f56..e431f1ef63 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -139,7 +139,7 @@ You have more flexibility when you create the database context in this way, for - You can choose not to use EF Core context pooling, which may be necessary in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) - You can use EF Core context factories or change the lifetime for the EF services. For more information, see [Use EF Core context factories in .NET Aspire](#use-ef-core-context-factories-in-net-aspire) - You can use dynamic connection strings. For more information, see [Use EF with dynamic connection strings in .NET Aspire](#use-ef-with-dynamic-connection-strings-in-net-aspire) -- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) that depend on DI services to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-interceptors-in-net-aspire) +- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) that depend on DI services to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-interceptors-with-net-aspire) By default, a context configured this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: From 7d8b75834018cb575cb5d56d22167fa31968cacd Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Wed, 26 Feb 2025 16:00:39 +0000 Subject: [PATCH 12/21] Integrated feedback from @IEvangelist. --- docs/database/use-entity-framework-db-contexts.md | 8 ++++---- docs/zones/zone-pivot-groups.yml | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index e431f1ef63..66bd10f41b 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -217,7 +217,7 @@ EF interceptors allow developers to hook into and modify database operations at Interceptors that depend on DI services are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: -:::zone-pivot="sql-server-ef" +:::zone pivot="sql-server-ef" ```csharp builder.Services.AddDbContextPool((serviceProvider, options) => @@ -235,7 +235,7 @@ builder.EnrichSqlServerDbContext( ``` :::zone-end -:::zone-pivot="postgresql-ef" +:::zone pivot="postgresql-ef" ```csharp builder.Services.AddDbContextPool((serviceProvider, options) => @@ -253,7 +253,7 @@ builder.EnrichNpgsqlDbContext( ``` :::zone-end -:::zone-pivot="oracle-ef" +:::zone pivot="oracle-ef" ```csharp builder.Services.AddDbContextPool((serviceProvider, options) => @@ -271,7 +271,7 @@ builder.EnrichOracleDatabaseDbContext( ``` :::zone-end -:::zone-pivot="mysql-ef" +:::zone pivot="mysql-ef" ```csharp builder.Services.AddDbContextPool((serviceProvider, options) => diff --git a/docs/zones/zone-pivot-groups.yml b/docs/zones/zone-pivot-groups.yml index 37d79da11c..51d0a6d626 100644 --- a/docs/zones/zone-pivot-groups.yml +++ b/docs/zones/zone-pivot-groups.yml @@ -64,14 +64,14 @@ groups: - id: nunit title: NUnit - id: entity-framework-client-integration - title: .NET Aspire Entity Framework client integration - prompt: Choose a .NET Aspire Entity Framework client integration + title: .NET Aspire Entity Framework Core client integration + prompt: Choose a .NET Aspire Entity Framework Core client integration pivots: - id: sql-server-ef - title: SQL Server Entity Framework Core + title: SQL Server - id: postgresql-ef - title: PostgreSQL Entity Framework Core + title: PostgreSQL - id: oracle-ef - title: Oracle Entity Framework Core + title: Oracle - id: mysql-ef - title: Pomelo MySQL Entity Framework Core + title: Pomelo MySQL From 9475a4ef39c8b79f077fedf3936000de858f85fc Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Thu, 27 Feb 2025 14:48:00 +0000 Subject: [PATCH 13/21] Removed all uses of after feedback from @AndriySvyryd. --- .../use-entity-framework-db-contexts.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index 66bd10f41b..bac01d746a 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -23,7 +23,7 @@ The most commonly used .NET Aspire EF client integrations are: > [!NOTE] > In .NET Aspire, EF is implemented by client integrations, not hosting integrations. So, for example, to use EF with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. -## Use .NET Aspire to create a database context +## Use .NET Aspire to create an EF context In EF, a [**context**](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. @@ -31,7 +31,7 @@ The .NET Aspire EF client integrations each include an extension method named `A These .NET Aspire add context methods: -- Check that a database context of the same type is not already registered in the Dependency Injection (DI) container. +- Check that a context of the same type is not already registered in the Dependency Injection (DI) container. - Use the connection name you pass to the method to get the connection string from the application builder. This connection name must match the name used when adding the corresponding resource to the app host project. - Apply the `DbContext` options, if you passed them. - Add the specified `DbContext` to the DI container with context pooling enabled. @@ -40,7 +40,7 @@ These .NET Aspire add context methods: - Enable health checks. - Enable connection resiliency. -Use these .NET Aspire add context methods when you want a simple way to create the database context and don't yet need advanced EF customization. +Use these .NET Aspire add context methods when you want a simple way to create a context and don't yet need advanced EF customization. :::zone pivot="sql-server-ef" @@ -133,7 +133,7 @@ builder.Services.AddDbContextPool(options => :::zone-end -You have more flexibility when you create the database context in this way, for example: +You have more flexibility when you create the context in this way, for example: - You can reuse existing configuration code for the context without rewriting it for .NET Aspire. - You can choose not to use EF Core context pooling, which may be necessary in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) @@ -202,7 +202,7 @@ builder.EnrichMySqlDbContext( :::zone-end -Retrieve the database context from the DI container using the same code as the previous example: +Obtain the context from the DI container using the same code as the previous example: ```csharp public class ExampleService(ExampleDbContext context) @@ -300,7 +300,7 @@ Most microservices always connect to the same database with the same credentials - You might offer your service to multiple tenants and need to use a different database depending on which customer made the request. - You might need to authenticate the request with a different database user account depending on which customer made the request. -For these requirements, you can use code to formulate a **dynamic connection string** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF method to create the database context and then enrich it: +For these requirements, you can use code to formulate a **dynamic connection string** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF method to create the context and then enrich it: :::zone pivot="sql-server-ef" @@ -387,15 +387,15 @@ builder.EnrichMySqlDbContext( :::zone-end -The above code replaces the place holder `{DatabaseName}` in the connection string with the string `ContosoDatabase`, at run time, before it creates the database context and enriches it. +The above code replaces the place holder `{DatabaseName}` in the connection string with the string `ContosoDatabase`, at run time, before it creates the context and enriches it. ### Use EF Core context factories in .NET Aspire An EF context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should get the EF context, add the new customer and address entities to it, call , and then dispose the context. -In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to register a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the database context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to register a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. -Other application types, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different database context, within a single HTTP request and response. To implement this approach, you can register a context factory, by calling the EF method. This method also partners well with the .NET Aspire `Enrich\DbContext` methods: +Other application types, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different context, within a single HTTP request and response. To implement this approach, you can register a context factory, by calling the EF method. This method also partners well with the .NET Aspire `Enrich\DbContext` methods: :::zone pivot="sql-server-ef" @@ -417,7 +417,7 @@ builder.EnrichSqlServerDbContext( ```csharp builder.Services.AddPooledDbContextFactory(options => - options.UseNpgsql(connectionString + options.UseNpgsql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); builder.EnrichNpgsqlDbContext( @@ -433,7 +433,7 @@ builder.EnrichNpgsqlDbContext( ```csharp builder.Services.AddPooledDbContextFactory(options => - options.UseOracle(connectionString + options.UseOracle(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); builder.EnrichOracleDatabaseDbContext( @@ -449,7 +449,7 @@ builder.EnrichOracleDatabaseDbContext( ```csharp builder.Services.AddPooledDbContextFactory(options => - options.UseMySql(connectionString + options.UseMySql(builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."))); builder.EnrichMySqlDbContext( @@ -478,13 +478,13 @@ Contexts created from factories in this way aren't disposed of automatically bec ## Use EF context pooling in .NET Aspire -In EF Core a database context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a database context pool. +In EF Core a context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a context pool. Context pooling is a feature of EF Core. Contexts are created as normal but, when you dispose of one, it isn't destroyed but reset and stored in a pool. The next time your code creates a context, the stored one is returned to avoid the extra overhead of creating a new one. In a .NET Aspire consuming project, there are three ways to use context pooling: -- Use the .NET Aspire `Add\DbContext` methods to create the database context. These methods create a context pool automatically. +- Use the .NET Aspire `Add\DbContext` methods to create the context. These methods create a context pool automatically. - Call the EF method instead of the EF method. :::zone pivot="sql-server-ef" @@ -563,7 +563,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone-end -Remember to enrich the database context after using the last two methods, as described above. +Remember to enrich the context after using the last two methods, as described above. > [!IMPORTANT] > Only the base context state is reset when it's returned to the pool. If you've manually changed the state of the `DbConnection` or another service, you must also manually reset it. Additionally, context pooling prevents you from using `OnConfiguring` to configure the context. See [DbContext pooling](/ef/core/performance/advanced-performance-topics#dbcontext-pooling) for more information. From c64ab9654f6c42baca4039c5e0acc3058cdd0c4e Mon Sep 17 00:00:00 2001 From: AJ Matthews Date: Fri, 28 Feb 2025 12:53:29 +0000 Subject: [PATCH 14/21] Integrated feedback from @IEvangelist. --- .../use-entity-framework-db-contexts.md | 102 +++++++++--------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/use-entity-framework-db-contexts.md index bac01d746a..05df50a6fa 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/use-entity-framework-db-contexts.md @@ -8,39 +8,39 @@ zone_pivot_groups: entity-framework-client-integration # Use Entity Framework Core with .NET Aspire -In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework (EF) Object-Relational Mapping (ORM) framework to streamline the process further. +In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework Core(EF Core) object-relational mapper (O/RM) approach to streamline the process. -Developers use ORM frameworks to work with databases using code objects instead of SQL queries. EF automatically codes database interactions by generating SQL queries based on LINQ queries. EF supports various database providers, including SQL Server, PostgreSQL, and MySQL so it's easy to interact with relational databases while following object-oriented principles. +Developers use O/RMs to work with databases using code objects instead of SQL queries. EF Core automatically codes database interactions by generating SQL queries based on LINQ queries. EF Core supports various database providers, including SQL Server, PostgreSQL, and MySQL, so it's easy to interact with relational databases while following object-oriented principles. -The most commonly used .NET Aspire EF client integrations are: +The most commonly used .NET Aspire EF Core client integrations are: -- SQL Server Entity Framework Core integration -- PostgreSQL Entity Framework Core integration -- MySQL Pomelo Entity Framework Core integration -- Oracle Entity Framework Core integration -- Cosmos DB Entity Framework Core integration +- [Cosmos DB Entity Framework Core integration](azure-cosmos-db-entity-framework-integration.md) +- [MySQL Pomelo Entity Framework Core integration](mysql-entity-framework-integration.md) +- [Oracle Entity Framework Core integration](oracle-entity-framework-integration.md) +- [PostgreSQL Entity Framework Core integration](postgresql-entity-framework-integration.md) +- [SQL Server Entity Framework Core integration](sql-server-entity-framework-integration.md) > [!NOTE] -> In .NET Aspire, EF is implemented by client integrations, not hosting integrations. So, for example, to use EF with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. +> In .NET Aspire, EF Core is implemented by client integrations, not hosting integrations. So, for example, to use EF Core with a SQL Server database, you'd use the SQL Server hosting integration to create the SQL Server container and add a database to it. In the consuming microservices, when you want to use EF Core, choose the SQL Server Entity Framework Core integration instead of the SQL Server client integration. -## Use .NET Aspire to create an EF context +## Use .NET Aspire to create an EF Core context -In EF, a [**context**](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. +In EF Core, a [**context**](/ef/core/dbcontext-configuration/) is a class used to interact with the database. Contexts inherit from the class. They provide access to the database through properties of type `DbSet`, where each `DbSet` represents a table or collection of entities in the database. The context also manages database connections, tracks changes to entities, and handles operations like saving data and executing queries. -The .NET Aspire EF client integrations each include an extension method named `Add\DbContext`, where **\** is a string identifying the database product you are using. For example, for the SQL Server EF client integration, the method is named and for the PostgreSQL client integration, the method is named . +The .NET Aspire EF Core client integrations each include extension methods named `Add{DatabaseSystem}DbContext`, where **{DatabaseSystem}** is the name identifying the database product you're using. For example, consider the SQL Server EF Core client integration, the method is named and for the PostgreSQL client integration, the method is named . These .NET Aspire add context methods: -- Check that a context of the same type is not already registered in the Dependency Injection (DI) container. +- Check that a context of the same type isn't already registered in the dependency injection (DI) container. - Use the connection name you pass to the method to get the connection string from the application builder. This connection name must match the name used when adding the corresponding resource to the app host project. - Apply the `DbContext` options, if you passed them. - Add the specified `DbContext` to the DI container with context pooling enabled. -- Apply the recommended defaults, unless you've disabled them through the Aspire EF settings: +- Apply the recommended defaults, unless you've disabled them through the .NET Aspire EF Core settings: - Enable tracing. - Enable health checks. - Enable connection resiliency. -Use these .NET Aspire add context methods when you want a simple way to create a context and don't yet need advanced EF customization. +Use these .NET Aspire add context methods when you want a simple way to create a context and don't yet need advanced EF Core customization. :::zone pivot="sql-server-ef" @@ -49,7 +49,7 @@ builder.AddSqlServerDbContext(connectionName: "database"); ``` > [!TIP] -> For full information about SQL Server hosting and client integrations, see [.NET Aspire SQL Server Entity Framework Core integration](/dotnet/aspire/database/sql-server-entity-framework-integration). +> For more information about SQL Server hosting and client integrations, see [.NET Aspire SQL Server Entity Framework Core integration](sql-server-entity-framework-integration.md). :::zone-end :::zone pivot="postgresql-ef" @@ -59,7 +59,7 @@ builder.AddNpgsqlDbContext(connectionName: "database"); ``` > [!TIP] -> For full information about PostgreSQL hosting and client integrations, see [.NET Aspire PostgreSQL Entity Framework Core integration](/dotnet/aspire/database/postgresql-entity-framework-integration). +> For more information about PostgreSQL hosting and client integrations, see [.NET Aspire PostgreSQL Entity Framework Core integration](postgresql-entity-framework-integration.md). :::zone-end :::zone pivot="oracle-ef" @@ -69,7 +69,7 @@ builder.AddOracleDatabaseDbContext(connectionName: "database") ``` > [!TIP] -> For full information about Oracle Database hosting and client integrations, see [.NET Aspire Oracle Entity Framework Core integration](/dotnet/aspire/database/oracle-entity-framework-integration). +> For more information about Oracle Database hosting and client integrations, see [.NET Aspire Oracle Entity Framework Core integration](oracle-entity-framework-integration.md). :::zone-end :::zone pivot="mysql-ef" @@ -79,11 +79,11 @@ builder.AddMySqlDbContext(connectionName: "database"); ``` > [!TIP] -> For full information about MySQL hosting and client integrations, see [.NET Aspire Pomelo MySQL Entity Framework Core integration](/dotnet/aspire/database/mysql-entity-framework-integration). +> For more information about MySQL hosting and client integrations, see [.NET Aspire Pomelo MySQL Entity Framework Core integration](mysql-entity-framework-integration.md). :::zone-end -Obtain the `ExampleDbContext` object from the DI container in the same way as for any other service: +You obtain the `ExampleDbContext` object from the DI container in the same way as for any other service: ```csharp public class ExampleService(ExampleDbContext context) @@ -92,16 +92,17 @@ public class ExampleService(ExampleDbContext context) } ``` -## Use EF to add a context and then enrich it +## Use EF Core to add and enrich context -Alternatively, you can add a context to the DI container using the standard EF method, as commonly used in non-.NET Aspire projects: +Alternatively, you can add a context to the DI container using the standard EF Core method, as commonly used in non-.NET Aspire projects: :::zone pivot="sql-server-ef" ```csharp builder.Services.AddDbContextPool(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("database") - ?? throw new InvalidOperationException("Connection string 'database' not found."))); + var connectionString = builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."); + options.UseSqlServer(connectionString)); ``` :::zone-end @@ -109,8 +110,9 @@ builder.Services.AddDbContextPool(options => ```csharp builder.Services.AddDbContextPool(options => - options.UseNpgsql(builder.Configuration.GetConnectionString("database") - ?? throw new InvalidOperationException("Connection string 'database' not found."))); + var connectionString = builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."); + options.UseNpgsql(connectionString)); ``` :::zone-end @@ -118,8 +120,9 @@ builder.Services.AddDbContextPool(options => ```csharp builder.Services.AddDbContextPool(options => - options.UseOracle(builder.Configuration.GetConnectionString("database") - ?? throw new InvalidOperationException("Connection string 'database' not found."))); + var connectionString = builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."); + options.UseOracle(connectionString)); ``` :::zone-end @@ -127,8 +130,9 @@ builder.Services.AddDbContextPool(options => ```csharp builder.Services.AddDbContextPool(options => - options.UseMySql(builder.Configuration.GetConnectionString("database") - ?? throw new InvalidOperationException("Connection string 'database' not found."))); + var connectionString = builder.Configuration.GetConnectionString("database") + ?? throw new InvalidOperationException("Connection string 'database' not found."); + options.UseMySql(connectionString)); ``` :::zone-end @@ -136,16 +140,16 @@ builder.Services.AddDbContextPool(options => You have more flexibility when you create the context in this way, for example: - You can reuse existing configuration code for the context without rewriting it for .NET Aspire. -- You can choose not to use EF Core context pooling, which may be necessary in some circumstances. For more information, see [Use EF context pooling in .NET Aspire](#use-ef-context-pooling-in-net-aspire) -- You can use EF Core context factories or change the lifetime for the EF services. For more information, see [Use EF Core context factories in .NET Aspire](#use-ef-core-context-factories-in-net-aspire) -- You can use dynamic connection strings. For more information, see [Use EF with dynamic connection strings in .NET Aspire](#use-ef-with-dynamic-connection-strings-in-net-aspire) -- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) that depend on DI services to modify database operations. For more information, see [Use EF interceptors in .NET Aspire](#use-ef-interceptors-with-net-aspire) +- You can choose not to use EF Core context pooling, which may be necessary in some circumstances. For more information, see [Use EF Core context pooling in .NET Aspire](#use-ef-core-context-pooling-in-net-aspire) +- You can use EF Core context factories or change the lifetime for the EF Core services. For more information, see [Use EF Core context factories in .NET Aspire](#use-ef-core-context-factories-in-net-aspire) +- You can use dynamic connection strings. For more information, see [Use EF Core with dynamic connection strings in .NET Aspire](#use-ef-core-with-dynamic-connection-strings-in-net-aspire) +- You can use [EF Core interceptors](/ef/core/logging-events-diagnostics/interceptors) that depend on DI services to modify database operations. For more information, see [Use EF Core interceptors in .NET Aspire](#use-ef-core-interceptors-with-net-aspire) -By default, a context configured this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF client integration includes a method named `Enrich\DbContext`. These enrich context methods: +By default, a context configured this way doesn't include .NET Aspire features, such as telemetry and health checks. To add those features, each .NET Aspire EF Core client integration includes a method named `Enrich\DbContext`. These enrich context methods: -- Apply an EF settings object, if you passed one. +- Apply an EF Core settings object, if you passed one. - Configure connection retry settings. -- Apply the recommended defaults, unless you've disabled them through the .NET Aspire EF settings: +- Apply the recommended defaults, unless you've disabled them through the .NET Aspire EF Core settings: - Enable tracing. - Enable health checks. - Enable connection resiliency. @@ -211,11 +215,11 @@ public class ExampleService(ExampleDbContext context) } ``` -### Use EF interceptors with .NET Aspire +### Use EF Core interceptors with .NET Aspire -EF interceptors allow developers to hook into and modify database operations at various points during the execution of database queries and commands. You can use them to log, modify, or suppress operations with your own code. Your interceptor must implement one or more interface from the interface. +EF Core interceptors allow developers to hook into and modify database operations at various points during the execution of database queries and commands. You can use them to log, modify, or suppress operations with your own code. Your interceptor must implement one or more interface from the interface. -Interceptors that depend on DI services are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF method and call the method in the options builder: +Interceptors that depend on DI services are not supported by the .NET Aspire `Add\DbContext` methods. Use the EF Core method and call the method in the options builder: :::zone pivot="sql-server-ef" @@ -291,16 +295,16 @@ builder.EnrichMySqlDbContext( :::zone-end > [!NOTE] -> For more information about EF interceptors and their use, see [Interceptors](/ef/core/logging-events-diagnostics/interceptors). +> For more information about EF Core interceptors and their use, see [Interceptors](/ef/core/logging-events-diagnostics/interceptors). -### Use EF with dynamic connection strings in .NET Aspire +### Use EF Core with dynamic connection strings in .NET Aspire Most microservices always connect to the same database with the same credentials and other settings, so they always use the same connection string unless there's a major change in infrastructure. However, you may need to change the connection string for each request. For example: - You might offer your service to multiple tenants and need to use a different database depending on which customer made the request. - You might need to authenticate the request with a different database user account depending on which customer made the request. -For these requirements, you can use code to formulate a **dynamic connection string** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF method to create the context and then enrich it: +For these requirements, you can use code to formulate a **dynamic connection string** and then use it to reach the database and run queries. However, this technique isn't supported by the .NET Aspire `Add\DbContext` methods. Instead you must use the EF Core method to create the context and then enrich it: :::zone pivot="sql-server-ef" @@ -391,11 +395,11 @@ The above code replaces the place holder `{DatabaseName}` in the connection stri ### Use EF Core context factories in .NET Aspire -An EF context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should get the EF context, add the new customer and address entities to it, call , and then dispose the context. +An EF Core context is an object designed to be used for a single unit of work. For example, if you want to add a new customer to the database, you might need to add a row in the **Customers** table and a row in the **Addresses** table. You should get the EF Core context, add the new customer and address entities to it, call , and then dispose the context. -In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF method described above to register a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. +In many types of web application, such as ASP.NET applications, each HTTP request closely corresponds to a single unit of work against the database. If your .NET Aspire microservice is an ASP.NET application or a similar web application, you can use the standard EF Core method described above to register a context that is tied to the current HTTP request. Remember to call the .NET Aspire `Enrich\DbContext` method to gain health checks, tracing, and other features. When you use this approach, the context lifetime is tied to the web request. You don't have to call the method when the unit of work is complete. -Other application types, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different context, within a single HTTP request and response. To implement this approach, you can register a context factory, by calling the EF method. This method also partners well with the .NET Aspire `Enrich\DbContext` methods: +Other application types, such as ASP.NET Core Blazor, don't necessarily align each request with a unit of work, because they use dependency injection with a different service scope. In such apps, you may need to perform multiple units of work, each with a different context, within a single HTTP request and response. To implement this approach, you can register a context factory, by calling the EF Core method. This method also partners well with the .NET Aspire `Enrich\DbContext` methods: :::zone pivot="sql-server-ef" @@ -476,7 +480,7 @@ public class ExampleService(IDbContextFactory contextFactory) Contexts created from factories in this way aren't disposed of automatically because they aren't tied to an HTTP request lifetime. You must make sure your code disposes of them. In this example, the `using` code block ensures the disposal. -## Use EF context pooling in .NET Aspire +## Use EF Core context pooling in .NET Aspire In EF Core a context is relatively quick to create and dispose of so most applications can set them up as needed without impacting their performance. However, the overhead is not zero so, if your microservice intensively creates contexts, you may observe suboptimal performance. In such situations, consider using a context pool. @@ -485,7 +489,7 @@ Context pooling is a feature of EF Core. Contexts are created as normal but, whe In a .NET Aspire consuming project, there are three ways to use context pooling: - Use the .NET Aspire `Add\DbContext` methods to create the context. These methods create a context pool automatically. -- Call the EF method instead of the EF method. +- Call the EF Core method instead of the EF Core method. :::zone pivot="sql-server-ef" @@ -524,7 +528,7 @@ In a .NET Aspire consuming project, there are three ways to use context pooling: :::zone-end -- Call the EF method instead of the EF method. +- Call the EF Core method instead of the EF Core method. :::zone pivot="sql-server-ef" From 8ac40c5ac4177ae4d0aa7410b6eafd363fc50a47 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 09:35:37 -0600 Subject: [PATCH 15/21] Adjust the TOC --- docs/database/azure-cosmos-db-integration.md | 1 + docs/database/azure-postgresql-integration.md | 1 + ...ty-framework-core-integration-overview.md} | 26 +++---- docs/toc.yml | 73 +++++++++---------- 4 files changed, 51 insertions(+), 50 deletions(-) rename docs/database/{use-entity-framework-db-contexts.md => entity-framework-core-integration-overview.md} (97%) diff --git a/docs/database/azure-cosmos-db-integration.md b/docs/database/azure-cosmos-db-integration.md index 028cd25567..9b71cb9a04 100644 --- a/docs/database/azure-cosmos-db-integration.md +++ b/docs/database/azure-cosmos-db-integration.md @@ -206,6 +206,7 @@ The .NET Aspire Azure Cosmos DB integration currently doesn't support metrics by ## See also - [Azure Cosmos DB](https://azure.microsoft.com/services/cosmos-db) +- [.NET Aspire Cosmos DB Entity Framework Core integration](azure-cosmos-db-entity-framework-integration.md) - [.NET Aspire integrations overview](../fundamentals/integrations-overview.md) - [.NET Aspire Azure integrations overview](../azure/integrations-overview.md) - [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) diff --git a/docs/database/azure-postgresql-integration.md b/docs/database/azure-postgresql-integration.md index 9926a09074..ce3733a0fe 100644 --- a/docs/database/azure-postgresql-integration.md +++ b/docs/database/azure-postgresql-integration.md @@ -25,6 +25,7 @@ uid: dotnet/aspire/azure-postgresql-integration - [PostgreSQL docs](https://www.npgsql.org/doc/api/Npgsql.html) - [Azure Database for PostgreSQL](/azure/postgresql/) +- [.NET Aspire Azure PostgreSQL Entity Framework Core integration](azure-postgresql-entity-framework-integration.md) - [.NET Aspire PostgreSQL integration](postgresql-integration.md) - [.NET Aspire integrations](../fundamentals/integrations-overview.md) - [.NET Aspire GitHub repo](https://github.com/dotnet/aspire) diff --git a/docs/database/use-entity-framework-db-contexts.md b/docs/database/entity-framework-core-integration-overview.md similarity index 97% rename from docs/database/use-entity-framework-db-contexts.md rename to docs/database/entity-framework-core-integration-overview.md index 05df50a6fa..3a3aa02ad7 100644 --- a/docs/database/use-entity-framework-db-contexts.md +++ b/docs/database/entity-framework-core-integration-overview.md @@ -1,12 +1,12 @@ --- -title: Use Entity Framework Core with .NET Aspire -description: Learn how to optimize the performance of .NET Aspire Entity Framework integrations using their context objects. -ms.date: 02/21/2025 +title: Entity Framework Core overview +description: Learn how to optimize the performance of .NET Aspire Entity Framework Core integrations using their context objects. +ms.date: 03/03/2025 uid: database/use-entity-framework-db-contexts zone_pivot_groups: entity-framework-client-integration --- -# Use Entity Framework Core with .NET Aspire +# Entity Framework Core overview In a cloud-native solution, such as those .NET Aspire is built to create, microservices often need to store data in relational databases. .NET Aspire includes integrations that you can use to ease that task, some of which use the Entity Framework Core(EF Core) object-relational mapper (O/RM) approach to streamline the process. @@ -102,7 +102,7 @@ Alternatively, you can add a context to the DI container using the standard EF C builder.Services.AddDbContextPool(options => var connectionString = builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."); - options.UseSqlServer(connectionString)); + options.UseSqlServer(connectionString)); ``` :::zone-end @@ -112,7 +112,7 @@ builder.Services.AddDbContextPool(options => builder.Services.AddDbContextPool(options => var connectionString = builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."); - options.UseNpgsql(connectionString)); + options.UseNpgsql(connectionString)); ``` :::zone-end @@ -122,7 +122,7 @@ builder.Services.AddDbContextPool(options => builder.Services.AddDbContextPool(options => var connectionString = builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."); - options.UseOracle(connectionString)); + options.UseOracle(connectionString)); ``` :::zone-end @@ -132,7 +132,7 @@ builder.Services.AddDbContextPool(options => builder.Services.AddDbContextPool(options => var connectionString = builder.Configuration.GetConnectionString("database") ?? throw new InvalidOperationException("Connection string 'database' not found."); - options.UseMySql(connectionString)); + options.UseMySql(connectionString)); ``` :::zone-end @@ -310,7 +310,7 @@ For these requirements, you can use code to formulate a **dynamic connection str ```csharp var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") - ?? throw new InvalidOperationException("Connection string 'database' not found."); + ?? throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -331,7 +331,7 @@ builder.EnrichSqlServerDbContext( ```csharp var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") - throw new InvalidOperationException("Connection string 'database' not found."); + throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -352,7 +352,7 @@ builder.EnrichNpgsqlDbContext( ```csharp var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") - throw new InvalidOperationException("Connection string 'database' not found."); + throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -373,7 +373,7 @@ builder.EnrichOracleDatabaseDbContext( ```csharp var connectionStringWithPlaceHolder = builder.Configuration.GetConnectionString("database") - throw new InvalidOperationException("Connection string 'database' not found."); + throw new InvalidOperationException("Connection string 'database' not found."); connectionString = connectionStringWithPlaceHolder.Replace("{DatabaseName}", "ContosoDatabase"); @@ -574,7 +574,7 @@ Remember to enrich the context after using the last two methods, as described ab ## See also -- [Entity Framework documentation hub](/ef) +- [Entity Framework Core documentation hub](/ef/core) - [Tutorial: Connect an ASP.NET Core app to SQL Server using .NET Aspire and Entity Framework Core](/dotnet/aspire/database/sql-server-integrations) - [Apply Entity Framework Core migrations in .NET Aspire](/dotnet/aspire/database/ef-core-migrations) - [DbContext Lifetime, Configuration, and Initialization](/ef/core/dbcontext-configuration/) diff --git a/docs/toc.yml b/docs/toc.yml index da1303e86b..7cd8daf78e 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -117,12 +117,6 @@ items: href: database/sql-server-integrations.md - name: Connect to storage href: storage/azure-storage-integrations.md - - name: Database initialization and maintenance - items: - - name: Entity Framework Core migrations - href: database/ef-core-migrations.md - - name: Seed data in a database - href: database/seed-database-data.md - name: Messaging using .NET Aspire integrations displayName: messaging href: messaging/messaging-integrations.md @@ -144,13 +138,8 @@ items: - name: Azure Cache for Redis output caching href: caching/azure-cache-for-redis-output-caching-integration.md - name: Azure Cosmos DB - items: - - name: Azure Cosmos DB - EF Core - displayName: cosmos db,database,ef core - href: database/azure-cosmos-db-entity-framework-integration.md - - name: Azure Cosmos DB - displayName: cosmos db,database - href: database/azure-cosmos-db-integration.md + displayName: cosmos db,database + href: database/azure-cosmos-db-integration.md - name: Azure Event Hubs displayName: event hubs,messaging href: messaging/azure-event-hubs-integration.md @@ -161,13 +150,8 @@ items: displayName: key vault,security href: security/azure-security-key-vault-integration.md - name: Azure PostgreSQL - items: - - name: Azure PostgreSQL - EF Core - displayName: postgres,postgresql,database,flexible server,ef core,azure for database - href: database/azure-postgresql-entity-framework-integration.md - - name: Azure PostgreSQL - displayName: postgres,postgresql,database,flexible server,azure for database - href: database/azure-postgresql-integration.md + displayName: postgres,postgresql,database,flexible server,azure for database + href: database/azure-postgresql-integration.md - name: Azure OpenAI displayName: azure ai,openai href: azureai/azureai-openai-integration.md @@ -198,6 +182,34 @@ items: href: /dotnet/api/?term=Aspire.Hosting.Azure&view=dotnet-aspire-9.0&preserve-view=true - name: Aspire.Azure API reference href: /dotnet/api/?term=Aspire.Azure&view=dotnet-aspire-9.0&preserve-view=true + - name: Entity Framework Core integrations + displayName: ef core,entity framework core,database + items: + - name: Overview + href: database/entity-framework-core-integration-overview.md + - name: Entity Framework Core migrations + href: database/ef-core-migrations.md + - name: Seed data in a database + displayName: seed data,ef core,scripts,entrypoint,init + href: database/seed-database-data.md + - name: Azure Cosmos DB Entity Framework Core integration + displayName: cosmos db,database,ef core + href: database/azure-cosmos-db-entity-framework-integration.md + - name: Azure PostgreSQL Entity Framework Core integration + displayName: postgresql,ef core + href: database/azure-postgresql-entity-framework-integration.md + - name: MySQL Pomelo Entity Framework Core integration + displayName: mysql pomelo,ef core + href: database/mysql-entity-framework-integration.md + - name: Oracle Entity Framework Core integration + displayName: oracle,ef core + href: database/oracle-entity-framework-integration.md + - name: PostgreSQL Entity Framework Core integration + displayName: postgresql,ef core + href: database/postgresql-entity-framework-integration.md + - name: SQL Server Entity Framework Core integration + displayName: sql server,ef core + href: database/sql-server-entity-framework-integration.md - name: Dapr items: - name: Overview @@ -217,19 +229,11 @@ items: displayName: mongodb,database href: database/mongodb-integration.md - name: MySQL - items: - - name: Pomelo MySQL - EF Core - displayName: pomelo mysql,database - href: database/mysql-entity-framework-integration.md - - name: MySQL - displayName: mysql,database - href: database/mysql-integration.md + displayName: mysql,database + href: database/mysql-integration.md - name: NATS displayName: nats,messaging href: messaging/nats-integration.md - - name: Oracle - EF Core - displayName: oracle,database,ef core - href: database/oracle-entity-framework-integration.md - name: Orleans items: - name: Overview @@ -275,13 +279,8 @@ items: displayName: seq,logging href: logging/seq-integration.md - name: SQL Server - items: - - name: SQL Database - EF Core - displayName: sql server,sql database,ef core - href: database/sql-server-entity-framework-integration.md - - name: SQL Database - displayName: sql server,sql database - href: database/sql-server-integration.md + displayName: sql server,sql database + href: database/sql-server-integration.md - name: Community Toolkit items: - name: Overview From 26c90594bbc2d5ce51aefd446123f391bafd91e2 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 10:38:10 -0600 Subject: [PATCH 16/21] Apply suggestions from code review --- docs/toc.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/toc.yml b/docs/toc.yml index 83d20611ab..19eb357dba 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -189,7 +189,7 @@ items: href: /dotnet/api/?term=Aspire.Hosting.Azure&view=dotnet-aspire-9.1&preserve-view=true - name: Aspire.Azure API reference href: /dotnet/api/?term=Aspire.Azure&view=dotnet-aspire-9.1&preserve-view=true - - name: Entity Framework Core integrations + - name: Entity Framework Core displayName: ef core,entity framework core,database items: - name: Overview @@ -199,22 +199,22 @@ items: - name: Seed data in a database displayName: seed data,ef core,scripts,entrypoint,init href: database/seed-database-data.md - - name: Azure Cosmos DB Entity Framework Core integration + - name: Azure Cosmos DB EF Core integration displayName: cosmos db,database,ef core href: database/azure-cosmos-db-entity-framework-integration.md - - name: Azure PostgreSQL Entity Framework Core integration + - name: Azure PostgreSQL EF Core integration displayName: postgresql,ef core href: database/azure-postgresql-entity-framework-integration.md - - name: MySQL Pomelo Entity Framework Core integration + - name: MySQL Pomelo EF Core integration displayName: mysql pomelo,ef core href: database/mysql-entity-framework-integration.md - - name: Oracle Entity Framework Core integration + - name: Oracle EF Core integration displayName: oracle,ef core href: database/oracle-entity-framework-integration.md - - name: PostgreSQL Entity Framework Core integration + - name: PostgreSQL EF Core integration displayName: postgresql,ef core href: database/postgresql-entity-framework-integration.md - - name: SQL Server Entity Framework Core integration + - name: SQL Server EF Core integration displayName: sql server,ef core href: database/sql-server-entity-framework-integration.md - name: Dapr From 21e59b823b5269d9c9d519bdfbc0661a604f246e Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 10:45:10 -0600 Subject: [PATCH 17/21] Update docs/toc.yml From 06da5810c5948a4a2850e315a7c6d51776bfb1e5 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 10:46:30 -0600 Subject: [PATCH 18/21] Apply suggestions from code review --- docs/toc.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/toc.yml b/docs/toc.yml index 19eb357dba..1a50b98c62 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -318,8 +318,6 @@ items: href: community-toolkit/hosting-data-api-builder.md - name: EventStore href: community-toolkit/hosting-eventstore.md - - name: Use Entity Framework Core for database interaction - href: database/use-entity-framework-db-contexts.md - name: Aspire.Hosting API reference href: /dotnet/api/?term=Aspire.Hosting&view=dotnet-aspire-9.1&preserve-view=true From c8b49ed8767685ecdc5581e3c0e4744e362ce10c Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 10:47:10 -0600 Subject: [PATCH 19/21] Update docs/toc.yml --- docs/toc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/toc.yml b/docs/toc.yml index 1a50b98c62..d230cf9778 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -194,7 +194,7 @@ items: items: - name: Overview href: database/entity-framework-core-integration-overview.md - - name: Entity Framework Core migrations + - name: Apply migrations href: database/ef-core-migrations.md - name: Seed data in a database displayName: seed data,ef core,scripts,entrypoint,init From 2acd8f8f47db8fdc550bba8754fb6e4120c9d8a6 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 11:07:11 -0600 Subject: [PATCH 20/21] Reorder Dapr and Elasticsearch in TOC --- docs/toc.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/toc.yml b/docs/toc.yml index d230cf9778..3cdbf47785 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -189,6 +189,15 @@ items: href: /dotnet/api/?term=Aspire.Hosting.Azure&view=dotnet-aspire-9.1&preserve-view=true - name: Aspire.Azure API reference href: /dotnet/api/?term=Aspire.Azure&view=dotnet-aspire-9.1&preserve-view=true + - name: Dapr + items: + - name: Overview + href: frameworks/dapr.md + - name: Dapr integration sample + href: /samples/dotnet/aspire-samples/aspire-dapr/ + - name: Elasticsearch + displayName: elasticsearch,search + href: search/elasticsearch-integration.md - name: Entity Framework Core displayName: ef core,entity framework core,database items: @@ -217,15 +226,6 @@ items: - name: SQL Server EF Core integration displayName: sql server,ef core href: database/sql-server-entity-framework-integration.md - - name: Dapr - items: - - name: Overview - href: frameworks/dapr.md - - name: Dapr integration sample - href: /samples/dotnet/aspire-samples/aspire-dapr/ - - name: Elasticsearch - displayName: elasticsearch,search - href: search/elasticsearch-integration.md - name: Keycloak (Preview) displayName: security,openid connect,single sign-on,sso,identity,federation,account management href: authentication/keycloak-integration.md From ff22f432d77f117b82f676eb3e167a90dde94f8d Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 3 Mar 2025 12:02:00 -0600 Subject: [PATCH 21/21] Simplify EF Core integration names in TOC --- docs/toc.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/toc.yml b/docs/toc.yml index 3cdbf47785..aee306f4d9 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -208,22 +208,22 @@ items: - name: Seed data in a database displayName: seed data,ef core,scripts,entrypoint,init href: database/seed-database-data.md - - name: Azure Cosmos DB EF Core integration + - name: Azure Cosmos DB displayName: cosmos db,database,ef core href: database/azure-cosmos-db-entity-framework-integration.md - - name: Azure PostgreSQL EF Core integration + - name: Azure PostgreSQL displayName: postgresql,ef core href: database/azure-postgresql-entity-framework-integration.md - - name: MySQL Pomelo EF Core integration + - name: MySQL Pomelo displayName: mysql pomelo,ef core href: database/mysql-entity-framework-integration.md - - name: Oracle EF Core integration + - name: Oracle displayName: oracle,ef core href: database/oracle-entity-framework-integration.md - - name: PostgreSQL EF Core integration + - name: PostgreSQL displayName: postgresql,ef core href: database/postgresql-entity-framework-integration.md - - name: SQL Server EF Core integration + - name: SQL Server displayName: sql server,ef core href: database/sql-server-entity-framework-integration.md - name: Keycloak (Preview)