Stashbox.AspNetCore.Multitenant
4.5.0
See the version list below for details.
dotnet add package Stashbox.AspNetCore.Multitenant --version 4.5.0
NuGet\Install-Package Stashbox.AspNetCore.Multitenant -Version 4.5.0
<PackageReference Include="Stashbox.AspNetCore.Multitenant" Version="4.5.0" />
paket add Stashbox.AspNetCore.Multitenant --version 4.5.0
#r "nuget: Stashbox.AspNetCore.Multitenant, 4.5.0"
// Install Stashbox.AspNetCore.Multitenant as a Cake Addin #addin nuget:?package=Stashbox.AspNetCore.Multitenant&version=4.5.0 // Install Stashbox.AspNetCore.Multitenant as a Cake Tool #tool nuget:?package=Stashbox.AspNetCore.Multitenant&version=4.5.0
stashbox-extensions-dependencyinjection
This repository contains Stashbox integrations for ASP.NET Core, .NET Generic Host and simple ServiceCollection based applications.
Package | Version |
---|---|
Stashbox.Extensions.Dependencyinjection | |
Stashbox.Extensions.Hosting | |
Stashbox.AspNetCore.Hosting | |
Stashbox.AspNetCore.Multitenant |
Options turned on by default:
- Automatic tracking and disposal of
IDisposable
andIAsyncDisposable
services. - Lifetime validation for
Developement
environments, but can be extended to all environment types.
Table of Contents
- ASP.NET Core
- .NET Generic Host
- ServiceCollection Based Applications
- Additional IServiceCollection Extensions
ASP.NET Core
The following example shows how you can integrate Stashbox (with the Stashbox.Extensions.Hosting
package) as the default IServiceProvider
implementation into your ASP.NET Core application:
ASP.NET Core 5
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(String[] args)
{
return Host.CreateDefaultBuilder(args)
.UseStashbox(container => // Optional configuration options.
{
// This one enables the lifetime validation for production environments too.
container.Configure(config => config.WithLifetimeValidation());
})
.ConfigureContainer<IStashboxContainer>((context, container) =>
{
// Execute a dependency tree validation.
if (context.HostingEnvironment.IsDevelopment())
container.Validate();
})
.ConfigureWebHostDefaults(
webBuilder => webBuilder
.UseStartup<Startup>());
}
}
You can also use the ConfigureContainer()
method in your Startup
class to use further configuration options:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Your service configuration.
}
public void ConfigureContainer(IStashboxContainer container)
{
// Your container configuration.
container.Configure(config => config.WithLifetimeValidation());
}
public void Configure(IApplicationBuilder app)
{
// Your application configuration.
}
}
ASP.NET Core 6
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseStashbox(container => // Optional configuration options.
{
// This one enables the lifetime validation for production environments too.
container.Configure(config => config.WithLifetimeValidation());
});
builder.Host.ConfigureContainer<IStashboxContainer>((context, container) =>
{
// Execute a dependency tree validation.
if (context.HostingEnvironment.IsDevelopment())
container.Validate();
});
Controller / View activation
By default the ASP.NET Core framework uses the DefaultControllerActivator
to instantiate controllers, but it uses the ServiceProvider
only for instantiating their constructor dependencies. This behaviour could hide important errors Stashbox would throw in case of a misconfiguration, so it's recommended to let Stashbox activate your controllers and views.
You can enable this by adding the following options to your service configuration:
ASP.NET Core 5
public void ConfigureServices(IServiceCollection services)
{
// For controllers only.
services.AddControllers()
.AddControllersAsServices();
// For controllers and views.
services.AddControllersWithViews()
.AddControllersAsServices()
.AddViewComponentsAsServices();
}
ASP.NET Core 6
// For controllers only.
builder.Services.AddControllers()
.AddControllersAsServices();
// For controllers and views.
builder.Services.AddControllersWithViews()
.AddControllersAsServices()
.AddViewComponentsAsServices();
Multitenant
The Stashbox.AspNetCore.Multitenant
package provides support for multitenant applications with a component called TenantDistributor
. It's responsible for the following tasks:
- Create / maintain the application level Root Container. This container is used to hold the default service registrations for your application.
- Configure / maintain tenant specific containers. These containers are used to override the default services with tenant specific registrations.
- Tenant identification. Determines the tenant Id based on the current context. To achieve that, you have to provide an
ITenantIdExtractor
implementation.
// The type used to extract the current tenant identifier.
// This implementation shows how to extract the tenant id from a HTTP header.
public class HttpHeaderTenantIdExtractor : ITenantIdExtractor
{
public Task<object> GetTenantIdAsync(HttpContext context)
{
if (!context.Request.Headers.TryGetValue("TENANT-ID", out var value))
return Task.FromResult<object>(null);
return Task.FromResult<object>(value.First());
}
}
ASP.NET Core 5
public static IHostBuilder CreateHostBuilder(String[] args)
{
return Host.CreateDefaultBuilder(args)
.UseStashboxMultitenant<HttpHeaderTenantIdExtractor>(
distributor => // The tenant distributor configuration options.
{
// The default service registration, it registers into the root container.
// It also could be registered into the default
// service collection through the ConfigureServices() api.
distributor.Register<IDependency, DefaultDependency>();
// Configure tenants.
distributor.ConfigureTenant("TenantA", container =>
// Register tenant specific service override
container.Register<IDependency, TenantASpecificDependency>());
distributor.ConfigureTenant("TenantB", container =>
// Register tenant specific service override
container.Register<IDependency, TenantBSpecificDependency>());
})
// The container parameter is the tenant distributor itself.
// Calling its Validate() method will verify the root container and each tenant.
.ConfigureContainer<IStashboxContainer>((context, container) =>
{
// Validate the root container and all tenants.
if (context.HostingEnvironment.IsDevelopment())
container.Validate();
})
.ConfigureWebHostDefaults(
webBuilder => webBuilder
.UseStartup<Startup>());
}
ASP.NET Core 6
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseStashboxMultitenant<HttpHeaderTenantIdExtractor>(
distributor => // The tenant distributor configuration options.
{
// The default service registration, it registers into the root container.
// It also could be registered into the default
// service collection through the ConfigureServices() api.
distributor.Register<IDependency, DefaultDependency>();
// Configure tenants.
distributor.ConfigureTenant("TenantA", container =>
// Register tenant specific service override
container.Register<IDependency, TenantASpecificDependency>());
distributor.ConfigureTenant("TenantB", container =>
// Register tenant specific service override
container.Register<IDependency, TenantBSpecificDependency>());
});
// The container parameter is the tenant distributor itself.
// Calling its Validate() method will verify the root container and each tenant.
builder.Host.ConfigureContainer<IStashboxContainer>((context, container) =>
{
// Validate the root container and all tenants.
if (context.HostingEnvironment.IsDevelopment())
container.Validate();
});
With this example setup, you can differentiate tenants in a per-request basis identified by a HTTP header, where every tenant gets their overridden services.
.NET Generic Host
The following example adds Stashbox (with the Stashbox.Extensions.Hosting
package) as the default IServiceProvider
implementation into your .NET Generic Host application:
public class Program
{
public static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.UseStashbox(container => // Optional configuration options.
{
// This one enables the lifetime validation for production environments too.
container.Configure(config => config.WithLifetimeValidation());
})
.ConfigureContainer<IStashboxContainer>((context, container) =>
{
// Execute a dependency tree validation.
if (context.HostingEnvironment.IsDevelopment())
container.Validate();
})
.ConfigureServices((context, services) =>
{
services.AddHostedService<Service>();
}).Build();
await host.RunAsync();
}
}
ServiceCollection Based Applications
With the Stashbox.Extensions.Dependencyinjection
package you can replace Microsoft's built-in dependency injection container with Stashbox. This package contains the core functionality used by the Stashbox.Extensions.Hosting
, Stashbox.AspNetCore.Hosting
and Stashbox.AspNetCore.Multitenant
packages.
The following example shows how you can use this integration:
public class Program
{
public static async Task Main(string[] args)
{
// Create the service collection.
var services = new ServiceCollection();
// Configure your service collection.
services.AddLogging();
services.AddOptions();
// Add your services.
services.AddScoped<IService, Service>();
// Integrate Stashbox with the collection and grab your ServiceProvider.
var serviceProvider = services.UseStashbox(container => // Optional configuration options.
{
container.Configure(config => config.WithLifetimeValidation());
});
// Start using the application.
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetService<IService>();
await service.DoSomethingAsync();
}
}
}
Or you can use your own StashboxContainer
to integrate with the ServiceCollection
:
public class Program
{
public static async Task Main(string[] args)
{
// Create your container.
var container = new StashboxContainer(config => // Optional configuration options.
{
config.WithLifetimeValidation();
});
// Create the service collection.
var services = new ServiceCollection();
// Configure your service collection.
services.AddLogging();
services.AddOptions();
// Add your services.
services.AddScoped<IService, Service>();
// Or add them through Stashbox.
container.RegisterScoped<IService, Service>();
// Integrate Stashbox with the collection.
services.UseStashbox(container);
// Execute a dependency tree validation.
container.Validate();
// Start using the application.
await using (var scope = container.BeginScope())
{
var service = scope.Resolve<IService>();
await service.DoSomethingAsync();
}
}
}
Additional IServiceCollection
Extensions
Most of Stashbox's service registration functionalities are available as extension methods of IServiceCollection
.
-
class Service2 : IService2 { private readonly IService service; public Service2(IService service) { this.service = service; } } var services = new ServiceCollection(); services.AddTransient<IService, Service>(); // Name-less registration. services.AddTransient<IService, AnotherService>("serviceName"); // Register dependency with name. services.AddTransient<IService2, Service2>(config => // Inject the named service as dependency. config.WithDependencyBinding<IService>( "serviceName" // Name of the dependency. ));
Service configuration with Stashbox's Fluent Registration API:
var services = new ServiceCollection(); services.AddTransient<IService, Service>(config => config.WithFactory<IDependency>(dependency => new Service(dependency)).AsImplementedTypes());
-
class ServiceDecorator : IService { private readonly IService decorated; public ServiceDecorator(IService service) { this.decorated = service; } } var services = new ServiceCollection(); services.AddTransient<IService, Service>(); services.Decorate<IService, ServiceDecorator>();
-
var services = new ServiceCollection(); services.ScanAssemblyOf<IService>( // Set a filter for which types should be excluded/included in the registration process. // In this case, only the publicly available types are selected from the assembly. type => type.IsPublic, // The service type selector. Used to filter which interface or base types the implementation should be mapped to. // In this case, we are registering only by interfaces. (implementationType, serviceType) => serviceType.IsInterface, false, // Do not map services to themselves. E.g: Service -> Service. config => { // Register IService instances as scoped. if (config.ServiceType == typeof(IService)) config.WithScopedLifetime(); } );
-
class CompositionRoot : ICompositionRoot { public void Compose(IStashboxContainer container) { container.Register<IService, Service>(); } } var services = new ServiceCollection(); services.ComposeBy<CompositionRoot>(); // Or let Stashbox find all composition roots in an assembly. services.ComposeAssembly(typeof(CompositionRoot).Assembly);
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.1 is compatible. |
-
.NETCoreApp 3.1
- Stashbox.Extensions.Dependencyinjection (>= 4.5.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Stashbox.AspNetCore.Multitenant:
Package | Downloads |
---|---|
Stashbox.AspNetCore.Testing
Stashbox extension for writing integration tests for MVC applications. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
5.6.0 | 159 | 8/21/2024 |
5.5.4 | 98 | 7/26/2024 |
5.5.3 | 148 | 4/10/2024 |
5.5.2 | 136 | 4/8/2024 |
5.5.1 | 131 | 4/2/2024 |
5.5.0 | 220 | 12/15/2023 |
5.4.0 | 193 | 11/19/2023 |
5.3.0 | 250 | 6/21/2023 |
5.2.2 | 248 | 6/13/2023 |
5.2.1 | 266 | 6/9/2023 |
5.2.0 | 559 | 6/5/2023 |
5.1.2 | 256 | 6/2/2023 |
5.1.1 | 247 | 6/1/2023 |
5.1.0 | 260 | 5/31/2023 |
5.0.0 | 248 | 5/28/2023 |
4.6.2 | 347 | 3/29/2023 |
4.6.1 | 326 | 3/29/2023 |
4.6.0 | 366 | 2/28/2023 |
4.5.3 | 711 | 1/26/2023 |
4.5.2 | 429 | 1/26/2023 |
4.5.1 | 377 | 1/20/2023 |
4.5.0 | 455 | 12/19/2022 |
4.4.0 | 401 | 12/6/2022 |
4.3.2 | 374 | 11/29/2022 |
4.3.1 | 490 | 10/14/2022 |
4.3.0 | 448 | 10/12/2022 |
4.2.3 | 447 | 9/9/2022 |
4.2.2 | 892 | 6/2/2022 |
4.2.1 | 504 | 5/16/2022 |
4.2.0 | 635 | 5/3/2022 |
4.1.2 | 526 | 4/10/2022 |
4.1.1 | 534 | 3/12/2022 |
4.1.0 | 519 | 3/7/2022 |
4.0.1 | 674 | 2/10/2022 |
4.0.0 | 510 | 2/9/2022 |
3.2.1 | 554 | 1/30/2022 |
3.2.0 | 450 | 12/5/2021 |
3.1.1 | 380 | 11/22/2021 |
3.1.0 | 379 | 11/22/2021 |
3.0.0 | 510 | 11/22/2021 |
2.11.4 | 711 | 5/26/2021 |
2.11.3 | 562 | 3/16/2021 |
2.11.2 | 441 | 1/31/2021 |
2.11.1 | 671 | 11/16/2020 |
2.11.0 | 617 | 11/15/2020 |