Repository navigation
Document null connection string guard for PublishAsMigrationBundle with Azure Postgres #17856
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
Closed
Closed
Changes from 2 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
fbe8c8d
Initial plan
Copilot 611fae2
Document EF.IsDesignTime guard for migration bundle connection strings
Copilot 5c256cb
Remove design-time connection strings doc per review
Copilot c37704b
Keep README troubleshooting section with inline EF.IsDesignTime example
Copilot cfcd00d
Replace documentation link placeholder with published EF migrations URL
Copilot 9db4986
Potential fix for pull request finding
AndriySvyryd f4b0b04
Remove learn.microsoft.com/dotnet/aspire link from Additional documen…
Copilot 814ab98
Fix formatting of DbContext error message
AndriySvyryd 54ca1da
Replace EF.IsDesignTime guard with null connection string check in RE…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
src/Aspire.Hosting.EntityFrameworkCore/docs/design-time-connection-strings.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| # Handling design-time connection strings when publishing migration bundles | ||
|
AndriySvyryd marked this conversation as resolved.
Outdated
|
||
|
|
||
| When you generate a migration bundle during `aspire publish` | ||
| (`PublishAsMigrationBundle(...)`), Aspire runs the EF Core design-time tooling | ||
| (`dotnet ef migrations bundle`) against your startup project. EF Core has to | ||
| create an instance of your `DbContext` at design time to discover the model. | ||
|
|
||
| If your `DbContext` is configured with a provider that **parses the connection | ||
| string eagerly** while the context is being created — for example the Azure | ||
| Npgsql integration (`EnrichAzureNpgsqlDbContext`) — design-time creation fails | ||
| because the value injected for the connection string at publish time is a | ||
| placeholder/manifest expression rather than a real connection string. The error | ||
| looks like this: | ||
|
|
||
| ``` | ||
| ✗ [ERR] Unable to create a 'DbContext' of type 'AppDbContext'. The exception | ||
| 'Format of the initialization string does not conform to specification starting | ||
| at index 0.' was thrown while attempting to create an instance. | ||
| ✗ Step 'api-migrations-generate-migration-bundle' failed. | ||
| ``` | ||
|
|
||
| A migration bundle does not need a real connection string at design time: the | ||
| bundle receives the connection string at **run time** via the `--connection` | ||
| argument that Aspire wires up automatically for the bundle container. So the fix | ||
| is to skip the connection-string parsing while EF Core is building the model at | ||
| design time. | ||
|
|
||
| ## Steps to fix the project | ||
|
|
||
| EF Core exposes `EF.IsDesignTime`, which is `true` while the design-time tooling | ||
| (including the `migrations bundle` command) is running. Use it to call a | ||
| different `UseNpgsql` overload and to skip `EnrichAzureNpgsqlDbContext` at design | ||
| time. | ||
|
|
||
| Given a project that registers the `DbContext` like this: | ||
|
|
||
| ```csharp | ||
| using Microsoft.EntityFrameworkCore; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
|
|
||
| public static IHostApplicationBuilder AddAppData(this IHostApplicationBuilder builder) | ||
| { | ||
| builder.Services.AddPooledDbContextFactory<AppDbContext>((sp, options) => | ||
| { | ||
| options.UseNpgsql(builder.Configuration.GetConnectionString("postgresdb")); | ||
| }); | ||
|
|
||
| builder.EnrichAzureNpgsqlDbContext<AppDbContext>(); | ||
|
|
||
| return builder; | ||
| } | ||
| ``` | ||
|
|
||
| 1. Call the parameterless `UseNpgsql()` overload when `EF.IsDesignTime` is | ||
| `true`. This lets EF Core build the model without parsing a connection string. | ||
| 2. Only call `EnrichAzureNpgsqlDbContext` when **not** at design time, because the | ||
| enrichment configures an Entra ID data source that parses the connection string. | ||
|
|
||
| ```csharp | ||
| using Microsoft.EntityFrameworkCore; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
|
|
||
| public static IHostApplicationBuilder AddAppData(this IHostApplicationBuilder builder) | ||
| { | ||
| builder.Services.AddPooledDbContextFactory<AppDbContext>((sp, options) => | ||
| { | ||
| if (EF.IsDesignTime) | ||
| { | ||
| // At design time (e.g. when generating a migration bundle) no real | ||
| // connection string is available. Use the parameterless overload so | ||
| // EF Core can build the model without parsing a connection string. | ||
| options.UseNpgsql(); | ||
| } | ||
| else | ||
| { | ||
| options.UseNpgsql(builder.Configuration.GetConnectionString("postgresdb")); | ||
| } | ||
| }); | ||
|
|
||
| if (!EF.IsDesignTime) | ||
| { | ||
| // EnrichAzureNpgsqlDbContext configures an Entra ID data source that | ||
| // parses the connection string, which is not available at design time. | ||
| builder.EnrichAzureNpgsqlDbContext<AppDbContext>(); | ||
| } | ||
|
|
||
| return builder; | ||
| } | ||
| ``` | ||
|
|
||
| With this change, `aspire publish` generates the migration bundle successfully, | ||
| and the deployed bundle still receives the real connection string at run time via | ||
| the `--connection` argument that Aspire injects into the bundle container. | ||
|
|
||
| > The same pattern applies to any provider that parses the connection string while | ||
| > the `DbContext` is being created (for example a custom `NpgsqlDataSource`). Guard | ||
| > the connection-string-dependent configuration with `EF.IsDesignTime`. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.