-
Notifications
You must be signed in to change notification settings - Fork 223
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
Using NpgsqlDataSourceBuilder with EF Core Provider #3117
Comments
First, you're right that general documentation and guidance on using NpgsqlDataSource with EF is lacking... This is indeed something I need to address. Now, it's a bit odd to use AddNpgsqlDataSource to configure a data source in DI, only to then get it out as in the following code: services.AddNpgsqlDataSource(
ConnectionStringBuilder.Build(hostContext.Configuration),
builder =>
{
builder.EnableDynamicJson();
});
var dataSource = services.BuildServiceProvider().GetRequiredService<NpgsqlDataSource>(); It's easier to simply construct it yourself as described in the Npgsql 7.0 release notes: var dataSource = new NpgsqlDataSourceBuilder(ConnectionStringBuilder.Build(hostContext.Configuration))
{
builder.EnableDynamicJson();
} There's a simplification where you use AddNpgsqlDataSource(), and then omit passing the data source explicitly to UseNpgsql() (it will find it in DI explicitly for you). However, there's currently an issue in cases where there's more than one data source in the application - if that's your case avoid doing that. Next thing... When using DI and ASP.NET, you don't typically define OnConfiguring on your DbContext class, but rather get IDbContextOptions injected via its constructor (see the general EF docs for ASP.NET/DI). Since you pass the data source to UseNpgsql() in AddDbContext, those options you get - and pass to the base constructor - will already contain the data source. In other words, there's no need for you to deal with the data source in your DbContext class in any way.
No. NpgsqlDataSource conceptually represents a connection pool, and is meant to be fully thread-safe and usable from multiple threads.
See above for some comments.
I'm not sure what your specific problem with the owned entity mapping - you wrote "EF Core was failing to track the child objects because they lack primary keys (or any identity at all)", but I'm not sure what exactly that refers to. I suggest opening an issue with a minimal repro on the EF repo to make sure you're doing things right, before assuming you need to use the JSON POCO mapping.
Yes, I indeed agree work on the guidance is (sorely) needed. Unfortunately it may take a bit of time before I'm able to get around to it...
When you use AddNpgsqlDataSource() to register an NpgsqlDataSource in DI, that data source automatically logs via the ILoggerFactory registered in the same DI container; this is likely what you're referring to (that happens in addition to logging occuring at the EF level). You can configure logging in AddNpgsqlDataSource() explicitly to prevent that, or if you stop using AddNpgsqlDataSource() it won't happen. |
We've been successfully using the data source builder with DI since it was first introduced. For NET8 we needed to configure more options (ie. DynamicJson) but it's been working fine. The only problem we've hit (which is unrelated to the builder, but similar to one you call out) is that we can't use Even more advanced scenarios (like a periodic passsword provider that itself needs values/options from DI) were solvable with a minimal amount of code. My point is that it's certainly possible to use the builder without a complicated setup, or building the service povider beforehand (which you shouldn't do anyways and likely recieved an analyzer warning for). The one problem you may hit--which is one we did--would be in tests if you connect to many different servers. In our case we get a new server (docker container) per test class. The ManyServiceProviders warning will eventually rear its head, but we've been advised by the team that it's expected and safe to silence in those cases. |
Yep, that should be fine - and is also expected to go away soon (#3086). |
Excellent!
We only use one data source, so this will work fine for us. This will mean I don't need to pull the data source out of DI just after registering it, like you said. As you noted, I was under the impression that the Npgsql configuration needed to be done both in the DI container setup and in
I will keep this in mind if I decide to try again at a later date. In the meantime, maybe this will provide a better understanding of what I ran into: I read through the info Microsoft has published on using JSON columns. I threw this into my modelBuilder
.Entity<Job>()
.OwnsMany(job => job.Selections, ownedBuilder =>
{
ownedBuilder.ToJson("selections");
}); However, modelBuilder
.Entity<Job>()
.OwnsMany(job => job.Selections, ownedBuilder =>
{
ownedBuilder.ToJson("selections");
ownedBuilder.OwnsOne(sel => sel.Path);
}); Now I have Maybe I'm missing something fundamental here, but it seems like this is an unsupported scenario. That aside, thanks for your feedback! I've updated the application to apply your suggestions and it's working fine (with EnableDynamicJson()` still in place). Your work on this project is much appreciated. |
Hello, @roji, You've forgot to mention that Npgsql.DependencyInjection should be added as a dependecy. And also no need to create local variable with datasource instance (which also produces lint warning in Rider) I'll post my code that worked, for ones who stumbles upon this issue:
builder.Services.AddNpgsqlDataSource(
builder.Configuration.GetConnectionString("YourDatasurceName")!,
pgBuilder =>
{
pgBuilder.Name="YourDatasurceName";
pgBuilder.EnableDynamicJson();
},
serviceKey: "YourDatasurceName");
builder.Services.AddDbContext<ObriyDbContext>((services,options) =>
options.UseNpgsql(services.GetRequiredKeyedService<DbDataSource>("YourDatasurceName")!)
); You can omit specifying serviceKey and user GetRequiredService instead, but that would become confusing if you decide to add second datasource later. |
My team's project currently makes use of the now-deprecated JSON POCO mapping in Npgsql. I did try to migrate to the
ToJson
version of the mapping. Unfortunately the requirement for owned entities caused problems. EF Core was failing to track the child objects because they lack primary keys (or any identity at all). As far as I can tell, this means that if we want a strongly-typed JSON column, we have no choice but to utilizeEnableDynamicJson
.This brings me to the main issue:
NpgsqlConnection.GlobalTypeMapper
is deprecated, and there is almost no documentation on usingNpgsqlDataSource
with the EF Core provider.At the time of this writing, the Npgsql EF Core Provider documentation makes no mention at all of
NpgsqlDataSource
. The only example I could find for setting up a data source is here which assumes the user is managing the database without a framework. It seems there aren't too many people using this configuration with EF Core yet, so I've been working on figuring out the right way to put the pieces together for the last few hours.I started with a fairly typical existing implementation bsed on the EF Core Getting Started documentation. Here's a sample of that:
Initially I tried creating the
NpgsqlDataSource
in theAnalyticsContext.OnConfiguring()
method. This probably would have worked if I was creating a singleDbContext
for the life of the application. However, I have a multi-threaded operation that requires me to spin up several newDbContext
s on the fly. During testing, I received theManyServiceProvidersCreatedWarning
.After a bit of searching, I was able to deduce that this was due to creating multiple
NpgsqlDataSources
and that the creation of that resource should go intoConfigureServices
instead. I discoveredAddNpgsqlDataSource
which only appears in the documentation in three places (Npgsql 7.0 Release Notes, Logging, Npgsql 8.0 Release Notes) - none of which actually describe how it's used, and none of which show it being used in conjunction with EF Core.I tried implementing it anyway, modifying the source as such:
In addition, for the multi-threaded phase of the application I created a factory:
This all appears to work but I'm not confident in the implementation because I don't have any way to see what an implementation should look like with EF Core.
To sum up, here are my questions:
NpgsqlDataSource
from DI with multipleDbContext
instances?NpgsqlDataSource
andDbContext
in a correct way?NpgsqlDataSource
? Further, can it include a section on how to properly useAddNpgsqlDataSource
? Maybe the mere presence of this issue will help steer future developers in the right direction, but of course that's not ideal.Npgsql.Command
toWarning
in my configuration.The text was updated successfully, but these errors were encountered: