-
Notifications
You must be signed in to change notification settings - Fork 5
Configuring Shells
A shell is configured with a name, a list of enabled features, and an optional configuration section. CShells supports multiple ways to provide this configuration.
Each shell has:
| Property | Description |
|---|---|
Name |
Unique shell identifier (e.g., "Default", "Acme") |
Features |
List of enabled feature names (or objects with inline settings) |
Configuration |
Shell-specific key/value configuration (hierarchical) |
The default configuration source. CShells reads from the "CShells" section by default.
{
"CShells": {
"Shells": {
"Default": {
"Features": {
"Core": {},
"Weather": {}
},
"Configuration": {
"WebRouting": {
"Path": ""
}
}
},
"Admin": {
"Features": {
"Core": {},
"Admin": { "MaxUsers": 100, "EnableAuditLog": true }
},
"Configuration": {
"WebRouting": {
"Path": "admin",
"RoutePrefix": "api/v1"
}
}
}
}
}
}Register with the default section name:
builder.AddShells(); // reads "CShells" sectionOr specify a custom section:
builder.AddShells("MyCustomSection");Define shells directly in Program.cs using the fluent AddShell API:
builder.AddShells(cshells =>
{
cshells.AddShell("Default", shell => shell
.WithFeatures("Core", "Weather")
.WithConfiguration("WebRouting:Path", ""));
cshells.AddShell("Admin", shell => shell
.WithFeature("Core")
.WithFeature("Admin", settings => settings
.WithSetting("MaxUsers", 100)
.WithSetting("EnableAuditLog", true))
.WithConfiguration("WebRouting:Path", "admin")
.WithConfiguration("WebRouting:RoutePrefix", "api/v1"));
});You can also use type-safe feature references:
cshells.AddShell("Default", shell => shell
.WithFeature<CoreFeature>()
.WithFeature<WeatherFeature>(f =>
{
// Set properties directly on the feature instance before ConfigureServices runs
f.ApiKey = "my-key";
f.TimeoutSeconds = 30;
}));The Action<TFeature> configurator runs after configuration binding but before ConfigureServices, so code always wins over appsettings.json values.
Load shells from individual JSON files. Useful for separating per-tenant configuration from the main config file and for cloud storage scenarios.
Install:
dotnet add package CShells.Providers.FluentStorage
dotnet add package FluentStorageCreate shell JSON files (e.g., Shells/Default.json):
{
"Name": "Default",
"Features": ["Core", "Weather"],
"Configuration": {
"WebRouting": {
"Path": ""
}
}
}Register the provider:
using FluentStorage;
using CShells.Providers.FluentStorage;
var shellsPath = Path.Combine(builder.Environment.ContentRootPath, "Shells");
var blobStorage = StorageFactory.Blobs.DirectoryFiles(shellsPath);
builder.AddShells(cshells =>
{
cshells.WithFluentStorageProvider(blobStorage);
});The FluentStorage provider supports Azure Blob Storage, AWS S3, and other backends in addition to local disk.
Implement IShellBlueprintProvider to load shell blueprints from any source (database, API, etc.):
using CShells.Lifecycle;
public class DatabaseShellBlueprintProvider : IShellBlueprintProvider
{
private readonly AppDbContext _dbContext;
public DatabaseShellBlueprintProvider(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<ProvidedBlueprint?> GetAsync(string name, CancellationToken cancellationToken = default)
{
var tenant = await _dbContext.Tenants
.SingleOrDefaultAsync(t => t.Id == name && t.IsActive, cancellationToken);
if (tenant is null)
return null;
return new ProvidedBlueprint(new DatabaseShellBlueprint(tenant));
}
public Task<BlueprintPage> ListAsync(BlueprintListQuery query, CancellationToken cancellationToken = default) =>
// Return paged BlueprintSummary rows for your source.
throw new NotImplementedException();
}Register it:
builder.AddShells(cshells =>
{
cshells.AddBlueprintProvider(sp => sp.GetRequiredService<DatabaseShellBlueprintProvider>());
});DatabaseShellBlueprint is your IShellBlueprint implementation that composes fresh ShellSettings for a tenant.
CShells uses exactly one blueprint provider per host. Code-first AddShell(...) registrations use the built-in in-memory provider; external sources register a single provider through AddBlueprintProvider(...) or a provider-specific extension.
builder.AddShells(cshells =>
{
// Code-first provider:
cshells.AddShell("Default", shell => shell.WithFeatures("Core"));
});
builder.AddShells(cshells =>
{
// External configuration provider:
cshells.WithConfigurationProvider(builder.Configuration);
});
builder.AddShells(cshells =>
{
// Custom external provider:
cshells.AddBlueprintProvider(sp => sp.GetRequiredService<DatabaseShellBlueprintProvider>());
});Do not mix AddShell(...) and AddBlueprintProvider(...) on the same host. If you need several backing stores, implement one custom IShellBlueprintProvider that combines them internally.
See Shell Blueprint Providers for provider patterns and examples.
The WebRouting configuration section controls how a shell is matched to incoming requests and how its endpoints are prefixed.
| Key | Description | Example |
|---|---|---|
WebRouting:Path |
URL path prefix for shell resolution (empty string = root) | "tenants/acme" |
WebRouting:Host |
Hostname for host-based resolution | "acme.example.com" |
WebRouting:RoutePrefix |
Additional prefix applied to all shell endpoints | "api/v1" |
Example: with Path = "acme" and RoutePrefix = "api/v1", an endpoint mapped at "products" is accessible at /acme/api/v1/products.
| Provider | Class | Use Case |
|---|---|---|
| Configuration | ConfigurationShellBlueprintProvider |
appsettings.json and any IConfiguration source |
| In-memory | InMemoryShellBlueprintProvider |
Code-first, testing |
| FluentStorage | FluentStorageShellBlueprintProvider |
Files on disk or cloud storage |