Finbuckle / Finbuckle.MultiTenant

Finbuckle.MultiTenant is an open-source multitenancy middleware library for .NET. It enables tenant resolution, per-tenant app behavior, and per-tenant data isolation.
https://www.finbuckle.com/multitenant
Apache License 2.0
1.3k stars 265 forks source link

ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider'. #173

Closed layinka closed 4 years ago

layinka commented 4 years ago

Hi, I am getting this error.

ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider'.

I am using the routestrategy with the MutliTenantIdentityDbContext. It works well after the pplication first starts, but as soon as you try to access the application after some time, you get this error

AndrewTriesToCode commented 4 years ago

Hi @layinka

Can you post your ConfigureServices and Configure from Startup so I can see how you've got things set up? Or can you post a link to the project in github?

A few questions:

layinka commented 4 years ago

Hi, unfortunately,its a private repo, i won't be able to share the github link.

When the error happens, where in the code is it happening? In an MVC controller?

Almost anywhere, once i try to access any page on the site. Since all requests eventually goes thro a controller,i will say its in a controller

how much time after starting does the error come up? minutes? hours?

Probably minutes, say about 5 minutes.

are you using SetTenantInfo at all? Most projects don't, but it might be related to the problem.

Yes i am, i am using a Share login of some sorts.

Also, I checked online and it seems the ServiceProvider instance is disposed after request, so if anyone still holds a reference to it, it will throw this error.

Don't know if this will help, this is a stacktrace

Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()    Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
    Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
    Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<T>(IServiceProvider provider)
    Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.GetTreeRouter()
    Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.RouteAsync(RouteContext context)
    Microsoft.AspNetCore.Routing.RouteCollection.RouteAsync(RouteContext context)
    Finbuckle.MultiTenant.Strategies.RouteStrategy.GetIdentifierAsync(object context)
    Finbuckle.MultiTenant.Strategies.MultiTenantStrategyWrapper<TStrategy>.GetIdentifierAsync(object context)

Here is my ConfigureServices

....
services.AddMultiTenant()
                .WithEFCoreStore<OrganisationDbContext, Organisation>()                
                .WithRouteStrategy(ConfigRoutes)
                .WithFallbackStrategy("g")
                .WithPerTenantOptions<CookieAuthenticationOptions>((options, tenantInfo) =>
                {

                    // Since we are using the route strategy configure each tenant
                    // to have a different cookie name and adjust the paths.
                    options.Cookie.Name = $"{tenantInfo.Id}_{options.Cookie.Name}";

                    options.Cookie.Path = $"/{tenantInfo.Identifier}";
                });

            // Required due to a bug in ASP.NET Core Identity (https://github.com/aspnet/Identity/issues/2019)
            services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme, options =>
            {
                // This will result in a path of /_tenant_/Identity/Account/Login
                options.LoginPath = $"{options.Cookie.Path}/Login";
                options.LogoutPath = $"{options.Cookie.Path}/Identity/Account/Logout";
            });
.....
AndrewTriesToCode commented 4 years ago

Thanks for providing more detail. I think the problem is how the route strategy captures IService provider. When TrySetTenantInfo is called is it before the MultiTenant middleware runs? Is the resetServiceProvider parameter set to true?

AndrewTriesToCode commented 4 years ago

I believe we are seeing this bug: https://github.com/aspnet/AspNetCore/issues/4148

I have a fix in mind which I will try to get out by friday.

AndrewTriesToCode commented 4 years ago

I'm still unable to reproduce this on my end. Can you tell me if the RouteStrategy sample project shows this behavior for you? Also which .Net Core runtime are you using and are you on Win/Linux/Mac?

Thanks!

AndrewTriesToCode commented 4 years ago

Ok, I'm pretty sure I have this narrowed down. Are you using Razor Pages or doing anything advanced with ActionDescriptors by any chance?

layinka commented 4 years ago

Hmmm, Not sure, but using the Asp.Net identity , that uses razor pages.

And Yes, i am using the Routestrategy

AndrewTriesToCode commented 4 years ago

Ok, I am able to reproduce it using razor pages. It wasn't the issue I linked above. The route strategy is trying to use a IServiceProvider... but changes to Razor Pages files (or just after a few minutes) will invalidate the IServiceProvider the strategy was using. I have a PR submitted which will fix this and I will put out a release tomorrow. Just as a note when Asp.NET Core 3 comes out its routing works differently so this won't be an issue..