GithubHelp home page GithubHelp logo

Comments (4)

roji avatar roji commented on August 20, 2024 1

@rowanfreeman-acutro the main point to keep in mind, is that NpgsqlDataSources should be long lived - created once and used throughout the application. Your original code above creates an NpgsqlDataSource for each and every DbContext, which is bad.

We definitely need to do some work to improve multi-tenant scenario (especially with NpgsqlDataSource) - I hope to get to do work on this for 9.0. But in the meantime, take a look at this link, which discusses DbContext pooling and multi-tenancy.

In a nutshell, you generally need to have a singleton registry of NpgsqlDataSources - one per tenant; this registry is a singleton since as I wrote above, a data source instance should live throughout your application. Then, you need to have a way for your DbContext to get initialized with the correct NpgsqlDataSource instance - the link above shows how to do that for a DbContextFactory.

Note that currently, having different NpgsqlDataSource instances (one-per-tenant) causes some trouble inside EF itself - that's exactly what #3086 will fix for 9.0.

from efcore.pg.

rowanfreeman-acutro avatar rowanfreeman-acutro commented on August 20, 2024 1

Thanks @roji, great stuff. Yeah that's the path I originally went down. I had a singleton class that had a dictionary of a few NpgsqlDataSources - one for each tenant, so that they would be reused. I also tried using a static class for the same thing.

But no matter what I tried, including consulting the link you mentioned, I just couldn't get anything to work.

Eventually EF creates too many internal IServiceProviders and throws an exception/warning.

Thanks though, it sounds like 3086 will contribute to this becoming more viable.

from efcore.pg.

rowanfreeman-acutro avatar rowanfreeman-acutro commented on August 20, 2024

I've come up with a solution to this problem using an EF interceptor. But after spending so many hours researching this, I'm really skeptical about my solution because I haven't seen it proposed anywhere else.

But it works; I successfully connected to four different databases, depending on the IServiceScope, using EF and Npgsql. No warnings, no errors.

public sealed class TenantDbConnectionInterceptor(ITenantContextAccessor tenantContextAccessor)
    : DbConnectionInterceptor
{
    public override ValueTask<InterceptionResult> ConnectionOpeningAsync(
        DbConnection connection,
        ConnectionEventData eventData,
        InterceptionResult result,
        CancellationToken cancellationToken = new()
    ) => ValueTask.FromResult(ConnectionOpening(connection, eventData, result));

    public override InterceptionResult ConnectionOpening(
        DbConnection connection,
        ConnectionEventData eventData,
        InterceptionResult result
    )
    {
        var tenant = tenantContextAccessor.Tenant;
        var connectionString = tenant?.ConnectionString ?? string.Empty;

        connection.ConnectionString = connectionString;

        return result;
    }
}

Can anyone see a problem with this?

from efcore.pg.

roji avatar roji commented on August 20, 2024

I had a singleton class that had a dictionary of a few NpgsqlDataSources - one for each tenant, so that they would be reused.

Yep, that's a very good approach.

Eventually EF creates too many internal IServiceProviders and throws an exception/warning.

That warning just means that you're instantiating more than some fix number of service providers, to help users catch the scenario where they're creating a service provider for each and every DbContext (which would be very bad). If you have a fixed number of tenants, and each tenant has a single NpgsqlDataSource (which, before #3086, causes a single EF service provider to be created), then you can safely suppress the warning and just proceed.

from efcore.pg.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.