-
Notifications
You must be signed in to change notification settings - Fork 10.3k
[Advice] Tackling multi-tenancy with stock DI - open generics #2964
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
Comments
/cc @sebastienros |
We took this approach in Orchard Core, have you looked at how it's done there? I could suggest you to use Orchard Core directly as it will provide more than just multi-tenancy, things that you will probably need for SaaS applications (modularity, theming, admin panel, user/role management, permissions, openid, ...). PS: Orchard Core and Orchard Core CMS are two different sets of packages, you won't need the CMS (people are often confused with the distinction) |
@sebastienros Just reviewed how it is done in OrchardCore - its nice to know I was on the right track. I can see a couple of issues with the approach which I'm sure you've already considered
It's an interesting challenge to try and tackle - I'll take a further look at OrchardCore - although I like the challenge of building stuff for myself - as does anyone I guess :-) I know those issues I've mentioned have previously been closed and a lot of the development around DI is governed by what features other containers support, but is it worth looking into DI changes to support:
cc @pranavkm |
The lifetime of the singletons is interesting, the way we register them means that they are not disposed once a tenant is stopped or recycled. This actually raised some concerns in the DI that the team handled quickly. So if we pass a custom instance, the DI won't try to dispose it, it's our instance. There is still an issue that is open which is about being explicit during the registration to provide the behavior regarding disposal. So yes right now the way we do it is not ideal with open generics.
Nobody will blame you on this one, we love puzzles and programming ;) |
@Antaris @sebastienros Any further action needed here (i.e. actual DI code change or investigation)? |
@muratg There are two things that could be addressed as separate issues, and I think one of them is already tracked (for a long time though): |
I think one of the stumbling blocks that has forced this design is the inability to close a generic type using a delegate instead of an open type service definition, for instance:
Obviously, one of the kicks here is the delegates only pass the
Or perhaps a some sort of
I think a combination of those items is not out of the realms of possibilities given the cross-section of containers that we're trying to satisfy? |
If you can implement that on all the containers we support today then there's a chance, otherwise, it's a no. |
@davidfowl What's your official supported list of containers? I don't mind investigating? |
@sebastienros FYI, here is my current implementation: |
@Antaris we also have a PR to fix some edge cases. OrchardCMS/OrchardCore#1644 |
We have no real official list (as we don't provide support), but you can use this as a starting point https://github.com/aspnet/DependencyInjection#using-other-containers-with-microsoftextensionsdependencyinjection. |
We need to figure out if this built on top (and therefore works for all container adapters) or if it's built into the implementation. So far all of the solutions on top have caveats but it's where we currently are. Maybe a doc describing the state of things and the patterns we recommend with tradeoffs is the action item here. |
Uh oh!
There was an error while loading. Please reload this page.
Hi team,
I'm tackling a design around supporting multi-tenancy in my application. Up until now, I've entirely relied on the stock DI libraries for all my IoC needs. To handle multi-tenancy, I wanted to enable scenarios around service lifetime that segregates tenant-scoped services away from singletons, scoped and transient services.
I know saaskit has gone a long way to provide a generic approach to multienancy, but, they've specifically built there DI story around StructureMap, which I am looking to avoid doing. In a future ideal world, I'd like my approach to multi-tenancy to support any DI container that .NET Core supports and is
IServiceProvider
compatible.My current approach is to introduce a subclass of
ServiceDescriptor
: theTenantScopedServiceDescriptor
. This descriptor supports a fixed lifetime scope ofSingleton
, so if the mutli-tenancy feature is disabled, any services registered with myAddTenantScoped(...)
are singleton on the root provider.The way I am planning to handle tenant-scoped services, was to create a new
IServiceProvider
on request, once aTenantId
is resolved. This tenant-scoped provider is cached for the lifetime of the application, likeApplicationServices
is. Once I have the tenant-scoped service provider, the next logical step is to replaceRequestServices
with my own scoped instance from my tenant-scoped parent provider. This is handled through middleware.The way this works under the hood, is when creating the tenant-scoped provider, I enumerate through all the available service registrations, and clone the service collection - except where I encounter a Singleton descriptor (which is not my
TenantScopedServiceDescriptor
). For singleton services, I create a newServiceDescriptor
which essentially delegates the instance resolution back to my root provider. The net effect should mean that transient and scoped services still behave as intended, but tenant-scoped services are resolved from my tenant-specific provider, and singletons fromApplicationServices
:Where this design is falling down, is that I can't create a forwarding
ServiceDesriptor
for open generics, as it requires an explicit open generic implementation type:I was hoping that there was an existing overload for Add* which would support
Func<IServiceProvider, Type, object>
for instance resolution, providing the second argumentType
as the concrete service type being requested. I can see this has been raised previously in #588, #474 and #498 but each was closed without any real resolution.Even if that were possible, it might require some additional changes under the hood to allow open generics to be delegated to an instance factory method - but even that has pitfalls in terms of type assurance etc.
Does anyone have advice - I'm specifically looking to stick around the stock DI rather than pull in another DI container.
The text was updated successfully, but these errors were encountered: