Stashbox.AspNetCore.Multitenant 4.1.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package Stashbox.AspNetCore.Multitenant --version 4.1.1
NuGet\Install-Package Stashbox.AspNetCore.Multitenant -Version 4.1.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Stashbox.AspNetCore.Multitenant" Version="4.1.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Stashbox.AspNetCore.Multitenant --version 4.1.1
#r "nuget: Stashbox.AspNetCore.Multitenant, 4.1.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Stashbox.AspNetCore.Multitenant as a Cake Addin
#addin nuget:?package=Stashbox.AspNetCore.Multitenant&version=4.1.1

// Install Stashbox.AspNetCore.Multitenant as a Cake Tool
#tool nuget:?package=Stashbox.AspNetCore.Multitenant&version=4.1.1

stashbox-extensions-dependencyinjection

Appveyor build status GitHub Workflow Status Tests Sourcelink

This repository contains Stashbox integrations for ASP.NET Core, .NET Generic Host and simple ServiceCollection based applications.

Package Version
Stashbox.Extensions.Dependencyinjection NuGet Version
Stashbox.Extensions.Hosting NuGet Version
Stashbox.AspNetCore.Hosting NuGet Version
Stashbox.AspNetCore.Multitenant NuGet Version

Options turned on by default:

  • Automatic tracking and disposal of IDisposable and IAsyncDisposable services.
  • Lifetime validation for Developement environments, but can be extended to all environment types.

Table of Contents

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:

  1. Create / maintain the application level Root Container. This container is used to hold the default service registrations for your application.
  2. Configure / maintain tenant specific containers. These containers are used to override the default services with tenant specific registrations.
  3. 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 also could be registered into the default 
            // service collection through the ConfigureServices() api.
            distributor.RootContainer.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>());
        })
        .ConfigureContainer<TenantDistributor>((context, distributor) =>
        {
            // Validate the root container and all the tenants.
            if (context.HostingEnvironment.IsDevelopment())
                distributor.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 also could be registered into the default 
    // service collection through the ConfigureServices() api.
    distributor.RootContainer.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>());
});

builder.Host.ConfigureContainer<TenantDistributor>((context, distributor) =>
{
    // Validate the root container and all the tenants.
    if (context.HostingEnvironment.IsDevelopment())
        distributor.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.

  • Named service registration:

    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());
    
  • Service decoration:

    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>();
    
  • Assembly registration:

    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();
      }
    );
    
  • Composition root:

    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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.5.3 80 4/10/2024
5.5.2 73 4/8/2024
5.5.1 86 4/2/2024
5.5.0 175 12/15/2023
5.4.0 158 11/19/2023
5.3.0 217 6/21/2023
5.2.2 209 6/13/2023
5.2.1 221 6/9/2023
5.2.0 374 6/5/2023
5.1.2 216 6/2/2023
5.1.1 200 6/1/2023
5.1.0 224 5/31/2023
5.0.0 214 5/28/2023
4.6.2 311 3/29/2023
4.6.1 289 3/29/2023
4.6.0 333 2/28/2023
4.5.3 675 1/26/2023
4.5.2 391 1/26/2023
4.5.1 342 1/20/2023
4.5.0 423 12/19/2022
4.4.0 371 12/6/2022
4.3.2 350 11/29/2022
4.3.1 463 10/14/2022
4.3.0 426 10/12/2022
4.2.3 425 9/9/2022
4.2.2 863 6/2/2022
4.2.1 479 5/16/2022
4.2.0 610 5/3/2022
4.1.2 492 4/10/2022
4.1.1 507 3/12/2022
4.1.0 491 3/7/2022
4.0.1 647 2/10/2022
4.0.0 484 2/9/2022
3.2.1 516 1/30/2022
3.2.0 417 12/5/2021
3.1.1 357 11/22/2021
3.1.0 353 11/22/2021
3.0.0 484 11/22/2021
2.11.4 687 5/26/2021
2.11.3 536 3/16/2021
2.11.2 418 1/31/2021
2.11.1 644 11/16/2020
2.11.0 590 11/15/2020