Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'OnConfiguring' cannot be used to modify DbContextOptions when DbContext pooling is enabled #1601

Open
1 task done
dradovic opened this issue Sep 3, 2024 · 8 comments
Open
1 task done
Labels
⌚ Not Triaged Not triaged

Comments

@dradovic
Copy link

dradovic commented Sep 3, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I'm trying to migrate a working ASP.NET Core app to Aspire .NET. I've added SQL Server support in the following way:

  1. In the AppHost project, I've added <PackageReference Include="Aspire.Hosting.SqlServer" Version="8.2.0" /> and a bit of configuration:
var sql = builder.AddSqlServer("sql");
var sqldb = sql.AddDatabase("sqldb");
...
builder.AddProject...
    .WithReference(sqldb)...
  1. In the "project" itself, I've added <PackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" Version="8.2.0" /> and I have:
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddSqlServerDbContext<ApplicationDbContext>("sqldb"); // this is new in order to...
//builder.Services.AddDbContext<ApplicationDbContext>(); // ...replace this
...

On the first usage (injection) of ApplicationDbContext, I get:

System.InvalidOperationException
  HResult=0x80131509
  Message='OnConfiguring' cannot be used to modify DbContextOptions when DbContext pooling is enabled.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.Set[TEntity]()
   ...

The existing app never used the DbContext pooling feature, so I tried to disable it by adding:

"Aspire": {
    "Microsoft": {
        "EntityFrameworkCore": {
            "SqlServer": {
                "ConnectionString": "Server=localhost;Database=...;Trusted_Connection=True;MultipleActiveResultSets=True;Encrypt=False",
                "DbContextPooling": false,
                "DisableHealthChecks": true,
                "DisableTracing": false,
                "DisableMetrics": false
            }
        }
    }
},

to the AppHost's appsettings.json. However, the exception is still raised. Which makes me wonder if the "DbContextPooling": false is respected. Or should I put this configuration somewhere else? (I've tried my luck into add it to the project's appsettings but that didn't change anything either.)

Expected Behavior

With "DbContextPooling": false configured, I would expect that I don't get an error that relates to DbContext pooling.

Steps To Reproduce

No response

Exceptions (if any)

System.InvalidOperationException
HResult=0x80131509
Message='OnConfiguring' cannot be used to modify DbContextOptions when DbContext pooling is enabled.

.NET Version info

.NET SDK:
Version: 8.0.401
Commit: 811edcc344
Workload version: 8.0.400-manifests.57f7c351
MSBuild version: 17.11.4+37eb419ad

Runtime Environment:
OS Name: Windows
OS Version: 10.0.19045
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\8.0.401\

.NET workloads installed:
Configured to use loose manifests when installing new manifests.
[aspire]
Installation Source: SDK 8.0.400, VS 17.11.35219.272
Manifest Version: 8.2.0/8.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.2.0\WorkloadManifest.json
Install Type: Msi

Host:
Version: 8.0.8
Architecture: x64
Commit: 08338fcaa5

.NET SDKs installed:
5.0.408 [C:\Program Files\dotnet\sdk]
6.0.100 [C:\Program Files\dotnet\sdk]
6.0.133 [C:\Program Files\dotnet\sdk]
6.0.425 [C:\Program Files\dotnet\sdk]
7.0.120 [C:\Program Files\dotnet\sdk]
7.0.203 [C:\Program Files\dotnet\sdk]
7.0.410 [C:\Program Files\dotnet\sdk]
8.0.108 [C:\Program Files\dotnet\sdk]
8.0.400 [C:\Program Files\dotnet\sdk]
8.0.401 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
Not set

Anything else?

I'm developing using Visual Studio Professional 17.11.1.

@sliekens
Copy link

sliekens commented Sep 3, 2024

Have you tried using a key that matches your DbContext name?

{
  "Aspire": {
    "Microsoft": {
      "EntityFrameworkCore": {
        "ApplicationDbContext": { <-- match DbContext name
          "ConnectionString": "YOUR_CONNECTIONSTRING",
          "DbContextPooling": false,
          "DisableHealthChecks": true,
          "DisableTracing": true,
          "DisableMetrics": false
        }
      }
    }
  }
}

@dradovic
Copy link
Author

dradovic commented Sep 4, 2024

Based on your suggestion, I started playing around with configuration using appsettings and currently, I'm not managing to get Aspire to pick up any configuration 🤔
I'll play around more with it and report back. I'm pretty sure that I'm doing something wrong here - I just need to figure out what.

@dradovic
Copy link
Author

dradovic commented Sep 5, 2024

I've created a small repro repository: https://github.com/dradovic/repro-aspire-dbcontext

Playing around with it, I've realized that:

  1. In order for a DbContext to receive the connection string that points to the SQL Server container that is created by Aspire, I must call the constructor of the DbContext with the provided options: public BloggingContext(DbContextOptions options) : base(options) {...}. It took me quite a while to figure this out as it's not mentioned anywhere on https://learn.microsoft.com/en-us/dotnet/aspire/database/sql-server-entity-framework-integration. The docs should be improved there, IMHO.
  2. In order to use a different (already running) SQL Server instance (for example a local Windows SQL Server), I have to configure the connection string in the project and not in the AppHost. This is not what I want because it means that Aspire will still spin up a SQL Server container although it's not being used. So the question is how to configure the connection string in the AppHost so that it can be shared with all projects and that Aspire doesn't spin up a SQL Server container on its own. I suspect this can somehow be done using External Parameters but that's an investigation of its own that I haven't done yet (hints appreciated).
  3. And coming back to the DbContext pooling problem (which this issue is about): even in the project's appsettings I wasn't able to use the configuration provider approach. For some reasons the settings are simply not being picked up. In my repro, I've added comment there: <-- why is this ignored? on https://github.com/dradovic/repro-aspire-dbcontext/blob/main/AspireDbContext/appsettings.json. So obviously "DbContextPooling": false, will neither be picked up. I've tried with SqlServer, BloggingContext, SqlServer:BloggingContext and sqldb as a configuration section key, but none of these worked.
  4. The MicrosoftEntityFrameworkCoreSqlServerSettings used in the inlined configuration approach don't contain a DisableDbContextPooling property. I guess, it would make sense to add this to be consistent with what can be configured by other means.

So TL;DR: the question remains how to disable DbContext pooling.

@maddymontaquila
Copy link
Member

If you don't want to do pooling, you should be using the Enrich api https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.aspiresqlserverefcoresqlclientextensions.enrichsqlserverdbcontext?view=dotnet-aspire-8.0 - regardless we should put this in the docs somewhere

@maddymontaquila maddymontaquila transferred this issue from dotnet/aspire Sep 6, 2024
@dotnet-bot dotnet-bot added the ⌚ Not Triaged Not triaged label Sep 6, 2024
@maddymontaquila
Copy link
Member

Moving to docs but also we should add some XML info too

@dradovic
Copy link
Author

dradovic commented Sep 6, 2024

@maddymontaquila Thanks for the suggestion. However, EnrichSqlServerDbContext uses the same MicrosoftEntityFrameworkCoreSqlServerSettings that I already mentioned above in point 4. It has DisableRetry, DisableTracing but I don't see anything pooling related.

@davidfowl
Copy link
Member

Enrich doesn't enable pooling, you are supposed to use the Enrich* method with the normal Entity Framework AddDbContext method (where you get full control over the configuration). i.e.

Delete this:

builder.AddSqlServerDbContext<ApplicationDbContext>("sqldb"); // this is new in order to...

Replace it with:

builder.Services.AddDbContext<ApplicationDbContext>(...);
builder.EnrichSqlServerDbContext<ApplicationDbContext>();

@dradovic
Copy link
Author

dradovic commented Sep 6, 2024

@davidfowl alright! Makes sense. So one uses the normal EF AddDbContext to configure everything as usual (including interceptors which would answer dotnet/aspire#3069) and then with EnrichSqlServerDbContext we have the chance to configure how Aspire interacts with the EF DbContext.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⌚ Not Triaged Not triaged
Projects
None yet
Development

No branches or pull requests

5 participants