CShells.AspNetCore 0.0.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package CShells.AspNetCore --version 0.0.2
                    
NuGet\Install-Package CShells.AspNetCore -Version 0.0.2
                    
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="CShells.AspNetCore" Version="0.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CShells.AspNetCore" Version="0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="CShells.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CShells.AspNetCore --version 0.0.2
                    
#r "nuget: CShells.AspNetCore, 0.0.2"
                    
#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.
#:package CShells.AspNetCore@0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CShells.AspNetCore&version=0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=CShells.AspNetCore&version=0.0.2
                    
Install as a Cake Tool

Packages NuGet CShells Docs

Target Framework License NuGet Downloads

CShells

A lightweight, extensible shell and feature system for .NET projects that lets you build modular and multi-tenant apps with per-shell DI containers and config-driven features.

Features

  • Multi-shell architecture - Each shell has its own isolated DI container
  • Feature-based modularity - Features are discovered automatically via attributes
  • Dependency resolution - Features can depend on other features with topological ordering
  • Configuration-driven - Shells and their features are configured via appsettings.json
  • ASP.NET Core integration - Middleware for per-request shell resolution

Use Cases

CShells is useful whenever you want clear modular boundaries, configurable feature sets, and isolated dependency graphs inside a .NET application.

Modular Monoliths with Pluggable Features

Model each functional area (e.g., Core, Billing, Reporting) as a feature and group them into shells that can be enabled or disabled via configuration. This keeps a monolithic codebase modular and lets you turn features on or off without code changes.

Multitenant Apps with Per-Tenant Feature Toggles

Treat each tenant as a shell with its own configuration and feature set. You can roll out features gradually, offer different capabilities per tenant, and keep tenant-specific services (e.g., integrations, branding, limits) isolated in per-shell DI containers.

Single-Tenant Apps with Environment- or Plan-Based Features

Use shells to represent different plans (Basic, Pro, Enterprise) or environments (Development, Staging, Production), each enabling a different set of features. This lets you keep one codebase while varying behavior and dependencies based on environment, subscription level, or other criteria.

Modular Frameworks and Platforms (CMS, CRM, Orchard Core/ABP-like)

Build your own modular application framework where modules are implemented as features discovered at startup. CShells’ feature discovery and ordering, combined with per-shell DI, make it a good fit for CMSs, CRMs, ERP-style systems, and frameworks similar to Orchard Core or ABP.

White-Label SaaS and Branded Deployments

Model each brand or deployment as a shell with its own enabled features, configuration, and DI registrations. You can share the same core features while varying branding, integrations, or compliance-related components per shell.

Extensible Line-of-Business Apps with Plugins

Expose extension points as features that can be discovered from additional assemblies and loaded into shells. This enables plugin-style architectures where internal teams or third parties can add capabilities without modifying the core app.

API Gateways and Backend-for-Frontend (BFF) Layers

Use shells to represent different API surfaces (mobile, web, partner, admin) with their own middleware, endpoints, and policies. Each shell can have tailored dependencies and configuration while still sharing common infrastructure and hosting.

Gradual Modularization of Legacy Apps

Introduce CShells into an existing application and start moving functionality into features and shells incrementally. This allows you to modularize and isolate areas of a legacy system over time without a big-bang rewrite.

Quick Start

1. Create a Feature

Features implement IShellFeature (for service registration) or IWebShellFeature (for services + endpoints):

using CShells.Features;
using CShells.AspNetCore.Features;
using Microsoft.Extensions.DependencyInjection;

[ShellFeature("Core", DisplayName = "Core Services")]
public class CoreFeature : IWebShellFeature
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ITimeService, TimeService>();
    }

    public void MapEndpoints(IEndpointRouteBuilder endpoints, IHostEnvironment? environment)
    {
        endpoints.MapGet("", () => new { Message = "Hello from Core feature" });
    }
}

Features can depend on other features and access ShellSettings via constructor:

[ShellFeature("Weather", DependsOn = ["Core"], DisplayName = "Weather Feature")]
public class WeatherFeature(ShellSettings shellSettings) : IWebShellFeature
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IWeatherService, WeatherService>();
    }

    public void MapEndpoints(IEndpointRouteBuilder endpoints, IHostEnvironment? environment)
    {
        endpoints.MapGet("weather", (IWeatherService weatherService) =>
            weatherService.GetForecast());
    }
}

2. Configure Shells

Option A: Using appsettings.json (default section name: CShells):

{
  "CShells": {
    "Shells": [
      {
        "Name": "Default",
        "Features": [ "Core", "Weather" ],
        "Properties": {
          "CShells.AspNetCore.Path": ""
        }
      },
      {
        "Name": "Admin",
        "Features": [ "Core", "Admin" ],
        "Properties": {
          "CShells.AspNetCore.Path": "admin"
        }
      }
    ]
  }
}

Option B: Using JSON files with FluentStorage:

Create JSON files in a Shells folder (e.g., Default.json, Admin.json) with the same structure as above.

Option C: Code-first configuration:

builder.AddShells(cshells =>
{
    cshells.AddShell("Default", shell => shell
        .WithFeatures("Core", "Weather")
        .WithPath(""));

    cshells.AddShell("Admin", shell => shell
        .WithFeatures("Core", "Admin")
        .WithPath("admin"));

    cshells.WithInMemoryShells();
});

3. Register CShells in Program.cs

Simple setup (reads from appsettings.json):

var builder = WebApplication.CreateBuilder(args);

// Register CShells from configuration
builder.AddShells();

var app = builder.Build();

// Configure middleware and endpoints
app.MapShells();

app.Run();

FluentStorage setup (reads from Shells folder):

using FluentStorage;

var builder = WebApplication.CreateBuilder(args);

var shellsPath = Path.Combine(builder.Environment.ContentRootPath, "Shells");
var blobStorage = StorageFactory.Blobs.DirectoryFiles(shellsPath);

builder.AddShells(cshells =>
{
    cshells.WithFluentStorageProvider(blobStorage);
});

var app = builder.Build();
app.MapShells();
app.Run();

Advanced setup with custom resolvers:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCShellsAspNetCore(cshells =>
{
    cshells.WithConfigurationProvider(builder.Configuration);
    cshells.WithPathResolver(options =>
    {
        options.ExcludePaths = ["/api", "/health"];
    });
    cshells.WithHostResolver();
}, assemblies: [typeof(Program).Assembly]);

var app = builder.Build();
app.MapShells();
app.Run();

Key Capabilities

  • IWebShellFeature - Features can expose their own endpoints using MapEndpoints(), keeping all logic self-contained
  • Automatic endpoint routing - MapShells() handles middleware and endpoint registration in one call
  • Shell path prefixes - Routes are automatically prefixed based on the CShells.AspNetCore.Path property
  • Per-shell DI containers - Each shell has its own isolated service provider with shell-specific services
  • Multiple configuration sources - Configure shells via appsettings.json, external JSON files, or code
  • Flexible shell resolution - Built-in path and host resolvers, plus extensibility for custom strategies
  • Feature dependencies - Features can depend on other features with automatic topological ordering
  • Constructor injection of ShellSettings - Features can access their shell's configuration via constructor
  • Runtime shell management - Add, update, or remove shells at runtime without restarting the application

Configuration

Shell Settings Providers

CShells supports multiple ways to configure shells:

1. Configuration-based (appsettings.json)
builder.AddShells(); // Uses default "CShells" section
// or
builder.AddShells("MyCustomSection");
// or
builder.Services.AddCShellsAspNetCore(cshells =>
{
    cshells.WithConfigurationProvider(builder.Configuration, "CShells");
});
2. FluentStorage (JSON files from disk/cloud)
var blobStorage = StorageFactory.Blobs.DirectoryFiles("./Shells");
builder.AddShells(cshells =>
{
    cshells.WithFluentStorageProvider(blobStorage);
});
3. Code-first (In-memory)
builder.AddShells(cshells =>
{
    cshells.AddShell("Default", shell => shell
        .WithFeatures("Core", "Weather")
        .WithPath("")
        .WithProperty("Title", "Default Site"));

    cshells.WithInMemoryShells();
});
4. Custom Provider
public class DatabaseShellSettingsProvider : IShellSettingsProvider
{
    public async Task<IEnumerable<ShellSettings>> GetAllAsync()
    {
        // Load from database, API, etc.
    }
}

builder.AddShells(cshells =>
{
    cshells.WithProvider<DatabaseShellSettingsProvider>();
});

Shell Context Scopes & Background Work

Shell context scopes provide a way to create scoped services within a shell's service provider. This is particularly useful for background workers or other services that need to execute work in the context of each shell.

Creating Shell Context Scopes

Use IShellContextScopeFactory to create scopes for shell contexts:

using CShells;

public class MyService
{
    private readonly IShellHost _shellHost;
    private readonly IShellContextScopeFactory _scopeFactory;

    public MyService(IShellHost shellHost, IShellContextScopeFactory scopeFactory)
    {
        _shellHost = shellHost;
        _scopeFactory = scopeFactory;
    }

    public void DoWork()
    {
        foreach (var shell in _shellHost.AllShells)
        {
            using var scope = _scopeFactory.CreateScope(shell);

            // Resolve scoped services from the shell's service provider
            var myService = scope.ServiceProvider.GetRequiredService<IMyService>();
            myService.Execute();
        }
    }
}

Background Worker Example

Here's an example of a background service that executes work for each shell:

using CShells;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public class ShellBackgroundWorker : BackgroundService
{
    private readonly IShellHost _shellHost;
    private readonly IShellContextScopeFactory _scopeFactory;
    private readonly ILogger<ShellBackgroundWorker> _logger;

    public ShellBackgroundWorker(
        IShellHost shellHost,
        IShellContextScopeFactory scopeFactory,
        ILogger<ShellBackgroundWorker> logger)
    {
        _shellHost = shellHost;
        _scopeFactory = scopeFactory;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            foreach (var shell in _shellHost.AllShells)
            {
                using var scope = _scopeFactory.CreateScope(shell);

                // Execute work in the shell's context
                _logger.LogInformation("Background work executed for shell '{ShellId}'", shell.Id.Name);

                // Resolve and use scoped services
                var service = scope.ServiceProvider.GetService<IMyService>();
                service?.Execute();
            }

            await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
        }
    }
}

Register your background worker in your service collection:

services.AddHostedService<ShellBackgroundWorker>();

Running the Sample App

The samples/CShells.Workbench project demonstrates a multi-tenant payment platform:

cd samples/CShells.Workbench
dotnet run

Then access:

  • https://localhost:5001/ - Default tenant (Basic tier - Stripe + Email)
  • https://localhost:5001/acme - Acme Corp (Premium tier - PayPal + SMS + Fraud Detection)
  • https://localhost:5001/contoso - Contoso Ltd (Enterprise tier - Stripe + Multi-channel + Fraud + Reporting)
  • https://localhost:5001/swagger - Swagger UI for all endpoints

See the Workbench README for detailed feature descriptions and API examples.

License

MIT License - see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.5 268 12/18/2025
0.0.4 173 12/5/2025
0.0.3 186 12/3/2025
0.0.2 658 12/1/2025
0.0.1 111 11/29/2025