Closed dazinator closed 5 years ago
Could you make a decorator for IServiceCollection that adds to the "child" service collection, but when enumerated or when requesting any items from the collection, it checks for items in both the root collection and the "child" collection?
It's an interesting idea, thanks. This could work. The child collection wouldn't see services added to the root container through other means (i.e if you added structure map, or autofac registratons directly) but it would see the services from the root IServiceCollection which would be all we need, so I think it would be workable.
One for the backlog ;-)
I've done a little bit more work on this recently.
I'm allowing you to pass an IServiceCollection
that contains the "default" services that you want to include in the tenant's IServiceCollection
before the per tenant services are then registered using AddMvc and the like.
public void ConfigureServices(IServiceCollection services)
{
// take a clone of the default "hosting" level services provided by the platform.
// We'll give these to dotnettency later so they can be also included in per tenant services.
var defaultServices = services.Clone();
services.AddMultiTenancy<Tenant>((builder) =>
{
builder.IdentifyTenantsWithRequestAuthorityUri()
.InitialiseTenant<TenantShellFactory>()
.AddAspNetCore() // I now also provide AddOwin()
.ConfigureTenantContainers((containerOptions) =>
{
containerOptions
.SetDefaultServices(defaultServices) // here it is.. just means these services will be in tenantServices already before below delegate is executed..
.Autofac((tenant, tenantServices) =>
{
tenantServices.AddRazorPages()
.AddNewtonsoftJson();
});
})
.ConfigureTenantMiddleware((tenantOptions) =>
{
// I now also provide OwinPipeline()
tenantOptions.AspNetCorePipeline((context, tenantAppBuilder) =>
{
tenantAppBuilder.Use(async (c, next) =>
{
Console.WriteLine("Entering tenant pipeline: " + context.Tenant?.Name);
await next.Invoke();
});
tenantAppBuilder.UseRouting();
tenantAppBuilder.UseAuthorization();
tenantAppBuilder.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
});
});
});
}
I'll address this issue in the form of a working razor pages example.
As per https://github.com/aspnet/Mvc/issues/8340
Need to re-think design of creating new Empty ServiceCollection's per tenant, when gathering services registrations.
The current approach is:
ServiceCollection
at ApplicationServices level to populate an autofac or structuremap container which is the "root" container, for which anIServiceProvider
is returned to asp.net core stack.ServiceCollection
and allowing the tenants services to be added to it.ServiceCollection
, to populate the newly created child container for that tenant with those additional registrations.Basically this approach let's you configure the child container by adding services to a new ServiceCollection for that tenant, which starts of empty.
The problem comes, because when
AddMvc
is called (and possibly other Extension Methods) they expect to find certain services allready registered in the ServiceCollection - likeIHostingEnvironment
. In my scenario, because when configuring a tenant's services, theServiceCollection
is only to collect delta registrations, it starts of empty, and therefore theAddMvc
configuration method can't find expected services and ends up configuring MVC wrong at the tenant level.I am not sure of the best way to resolve this just yet.