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.31k stars 267 forks source link

[Question] MultiTenantContext now readonly #816

Open malisancube opened 5 months ago

malisancube commented 5 months ago

The change in v7.0.0 makes MultiTenantContext to be readonly preventing changing of currect tenant info.

image

More details https://github.com/fullstackhero/dotnet-webapi-starter-kit/issues/925

Is there another way this can be done?

AndrewTriesToCode commented 5 months ago

Hi yes. It’s an implementation detail but you can inject IMultiTenantContextSetter. Look at the source in the middlewarere or TrySetTenantInfo and you’ll see how it can be used. I can’t recall right now but if it is only internal then I will make it public.

malisancube commented 5 months ago

Impressive!

douggish commented 5 months ago

I just ran into this same issue. I see IMultiTenantContextSetter is documented as an "implementation detail". Is there any plan to expose this functionality for general use? We are using the library in scenarios where the "entry point" is not an HTTP request.

AndrewTriesToCode commented 5 months ago

Yes it is available to inject as an interface currently. For your use case it is the right approach (or you could mutate the tenant info on the context but I don’t recommend that).

fbjerggaard commented 4 months ago

@AndrewTriesToCode Sorta related to this, when having a design time factory, how are we now supposed to inject the IMultiTenantContextAccessor? Earlier it was just a matter of creating a ITenantInfo and injecting, but since IMultiTenantContextAccessor only has internal implementations I can't really wrap my head around a solution - and the docs doesn't seem to have been updated to reflect this change

goforebroke commented 4 months ago

Hi Andrew,

Prior to the upgrade, I handled non http requests with something like this

 using var scope = _serviceProvider.CreateScope();

 var accessor = scope.ServiceProvider.GetRequiredService<IMultiTenantContextAccessor<AppTenantInfo>>();

 var multiTenantContext = new MultiTenantContext<AppTenantInfo>
 {
     TenantInfo = new AppTenantInfo()
     {
         Id = context.Message.TenantId.ToString(),
         Identifier = context.Message.Identifier.ToString(),
     }
 };

 acccessor.MultiTenantContext = multiTenantContext;

using the new interface, I will handle non http request as below?

 using var scope = _serviceProvider.CreateScope();

 var accessor = scope.ServiceProvider.GetRequiredService<IMultiTenantContextAccessor<AppTenantInfo>>();
 var setter = scope.ServiceProvider.GetRequiredService<IMultiTenantContextSetter>();

 var multiTenantContext = new MultiTenantContext<AppTenantInfo>
 {
     TenantInfo = new AppTenantInfo()
     {
         Id = context.Message.TenantId.ToString(),
         Identifier = context.Message.Identifier.ToString(),
     }
 };

 setter.MultiTenantContext = multiTenantContext;
AndrewTriesToCode commented 4 months ago

Hi @fbjerggaard and @goforebroke

Check out the Identity sample where I have the db design time factory in the data folder and and per-tenant scope for migration in program.cs.

I kept the old constructor signature for the mult-tenant db contexts specifically for these use cases despite the change in dependency injection. I should maybe update the docs on this topic.