Custom Connection factory#2268
Conversation
jzabroski
left a comment
There was a problem hiding this comment.
What's the reason for not providing a backward compatible constructor and just using IConnectionStringAccessor with a new MigrationConnectionFactory assigned in the constructor so that people have a clear warning that code is becoming obsolete?
|
The main reason is to avoid constructor ambiguity in DI, but I agree that keeping the old ctor is better for backward compatibility and gives users a clear obsolete warning instead of a breaking change. I can rework this so the existing Just to give some context: I need this functionality quite urgently in my current project, which is why I started the implementation. The ability to provide a fully configured connection through I’m happy to update the PR based on your feedback. |
|
The main thing I never settled is whether we should use the existing DbDataSource. dotnet/runtime#64812 If you look, someone contributed a related (still open) PR but I never pulled it in because I never spent the brain cycles to compare and contrast the two approaches, and there wasn't a ton of demands. But I absolutely am open to adding this, possibly this week, as I'm on a stay at home vacation right now. |
|
See #2002 |
|
Good point. I had not fully considered DbDataSource. This PR uses IMigrationConnectionFactory mainly because it works for all current provider targets and allows the runner to receive a ready IDbConnection regardless of whether it came from a connection string, token callback, NpgsqlDataSource, SqlConnection configuration, tenant resolution, etc. But I agree DbDataSource is probably the more idiomatic ADO.NET direction where it is available. It represents a configured data source/pool and is intended to hand out ready connections, while DbProviderFactory is more of a provider object factory. The runtime proposal explicitly argues for keeping DbProviderFactory and DbDataSource separate because they represent different concepts. Maybe the best approach is to support both:
Something like: Regarding #2002, I checked it. My understanding is that it solves the PostgreSQL dynamic password case by allowing a custom DbProviderFactory to be supplied for Postgres only. It looks intentionally minimal and delegates the dynamic password behavior to Npgsql. I also noticed the note there that DbProviderFactory may be reasonable internally but may not be the best public API surface. That is why I was thinking the current PR could be a more general version of the same idea:
I agree DbDataSource is worth considering, especially for NpgsqlDataSource and dynamic password scenarios. My preference would be to first support caller-configured/factory-created connections, because it solves the immediate need and also keeps the internal design flexible. Then DbDataSource could be added as a follow-up or additional overload that adapts into the same internal connection-factory abstraction. Something like: My thinking is that the immediate need is support for caller-provided / externally-created connections, because that unblocks cases where the connection has to be configured at runtime before FluentMigrator opens it. I will bring back the IConnectionStringAccessor' ctors first and mark them as |
|
I addressed the backward-compatibility and DI ambiguity feedback and pushed the updates. Main changes:
If you get a chance, could you please take another look? |
|
@jzabroski I like the idea behind this PR, as it would fix the outstanding issue where you need to configure the - for example - NpgsqlConnection in ways that cannot be expressed in the connection string. |
|
@fubar-coder does that mean you reviewed it and approve |
|
@Sherif-Ahmed can you add this to the PR public static IMigrationRunnerBuilder WithDataSource(
this IMigrationRunnerBuilder builder,
Func<IServiceProvider, DbDataSource> dataSourceFactory)
{
return builder.WithConnectionFactory((sp, _) =>
{
var dataSource = dataSourceFactory(sp);
return dataSource.CreateConnection();
});1
} |
Sorry, it was just meant as an opinion and a reason for why this PR is useful. |
|
@jzabroski I updated the PR accordingly. |
|
OK. I appreciate your effort there. I added some review comments on the code itself; I am still not done reviewing every line but making progress. I clicked submit on my current comments hoping that I won't have anything else to add. Some last changes requested:
|
Add net8.0 targeting for Runner.Core so the DbDataSource-based API is available on modern .NET targets while remaining excluded from net48 and netstandard2.0.
|
Thanks, that makes sense. My understanding is that DbDataSource is a better long-term abstraction than only passing an IDbConnection, because it can represent the configured data source/pool and leaves room for future provider features such as batching or reducing round-trips, but for now keeping the scope focused on configurable connection creation, while keeping the API compatible with adding more DbDataSource-based behavior later if you want to move in that direction. And yes, I used AI assistance to help compare the approaches and to help me express the analysis clearly, especially because English is not my mother tongue 😄. That said, I reviewed and adapted the conclusions against the actual FluentMigrator design and the ADO.NET APIs before applying anything.
So |
I didn't notice that; thanks for pointing that out!
👍 Thanks for updating everything. I have to fix the GitHub Actions then I will look into merging a bunch of outstanding PRs. There may be some merge conflicts with Gerard Smit's nativeaot/trimmable PR #2252 |
|
This will be included shortly via Copilot. It used a combination of your ideas and the other PR. |
|
Completed via #2320 |
This PR adds support for creating migration database connections through a configurable connection factory.
The main goal is to allow applications to provide fully configured
IDbConnectioninstances instead of relying only on a connection string. This is useful for scenarios where connection setup requires runtime logic or provider-specific configuration, such as Azure SQL access tokens, PostgreSQL dynamic passwords, tenant-specific connection resolution, or external secret stores.Changes
IMigrationConnectionFactory.WithConnectionFactory(...)toIMigrationRunnerBuilder.GenericProcessorBaseto create connections throughIMigrationConnectionFactory.IMigrationConnectionFactory..WithGlobalConnectionString(...)behavior.WithConnectionFactory(...).Example
Related to #1981 and PR #2002.
We can also support externally-owned connections later