Repository navigation
Dependency Injection
FileHub.DependencyInjection adds IServiceCollection helpers for registering any hub — Local, Memory, OCI, or custom. No driver assembly is referenced from it, so you pay only for what you use.
dotnet add package FileHub.DependencyInjectionusing FileHub.DependencyInjection;
using FileHub.Local;
services.AddFileHub<ILocalFileHub>(sp => new LocalFileHub(@"C:\data"));Registers two services pointing at the same instance: ILocalFileHub and IFileHub. Inject either.
| Overload | Registers | Use |
|---|---|---|
AddFileHub<T>(Func<IServiceProvider, T>, ServiceLifetime = Singleton) |
T + IFileHub
|
Driver-typed injection (e.g. ILocalFileHub). Default singleton. |
AddFileHub<T>(T instance) |
T + IFileHub (singleton) |
Prebuilt instance. |
AddFileHub(Func<IServiceProvider, IFileHub>, ServiceLifetime = Singleton) |
IFileHub only |
Driver-agnostic consumer code. |
AddFileHub(IFileHub instance) |
IFileHub (singleton) |
Prebuilt instance, no typed slot. |
// Scoped factory
services.AddFileHub<ILocalFileHub>(
sp => new LocalFileHub(@"C:\data"),
ServiceLifetime.Scoped);
// Prebuilt instance (always singleton)
services.AddFileHub<IMemoryFileHub>(new MemoryFileHub());
// Per-tenant single hub
services.AddFileHub(sp =>
{
var tenant = sp.GetRequiredService<ITenantContext>().Id;
return new LocalFileHub($@"C:\tenants\{tenant}");
}, ServiceLifetime.Scoped);Cloud drivers (OCI) should usually be singleton so the HTTP client is reused.
For multiple hubs, use AddNamedFileHubs. The callback receives a NamedFileHubsServiceBuilder; the extension wires up an INamedFileHubs registry (scoped) that you inject.
services.AddNamedFileHubs(b => b
.Register("reports", new MemoryFileHub())
.Register("logs", new LocalFileHub(@"C:\logs")));Register has two forms:
| Form | Lifetime | Use |
|---|---|---|
Register(name, IFileHub instance) |
Singleton | Prebuilt instance. |
Register(name, Func<IServiceProvider, IFileHub>, ServiceLifetime = Singleton) |
Configurable | Factory that sees the scope provider — supports tenant scoping. |
services.AddScoped<ITenantContext, HttpTenantContext>();
services.AddNamedFileHubs(b => b.Register(
"tenant",
sp => new LocalFileHub($@"C:\tenants\{sp.GetRequiredService<ITenantContext>().Id}"),
ServiceLifetime.Scoped));Each request scope gets its own hub; within a scope, GetByName("tenant") returns the same cached instance.
public class UploadsController(INamedFileHubs hubs)
{
public IActionResult Put(string name)
{
var hub = hubs.GetByName("tenant")!;
hub.Root.CreateFile(name);
return Ok();
}
}-
GetByName(name)→IFileHubornull(never throws; case-insensitive). -
GetRootByName(name)→ the rootDirectoryEntryornull.
When setup itself needs DI (e.g. IOptions<T>), the (sp, builder) => … overload exposes the root provider:
services.AddNamedFileHubs((sp, b) =>
{
var opts = sp.GetRequiredService<IOptions<StorageOptions>>().Value;
b.Register("reports", new LocalFileHub(opts.ReportsPath));
b.Register(
"tenant",
innerSp => new LocalFileHub(Path.Combine(
opts.TenantsRoot,
innerSp.GetRequiredService<ITenantContext>().Id)),
ServiceLifetime.Scoped);
});The outer sp runs once (singleton spec build); innerSp runs per lookup.
-
Registry is scoped. Inject
INamedFileHubsinto scoped/transient consumers. Singletons that hold it capture one scope — don't. -
Per-entry lifetime honoured. Singleton hubs live at root; scoped hubs are cached per scope on the registry; transient hubs are rebuilt on every
GetByName. - Disposal. Scoped hubs dispose with their scope; singleton hubs dispose with the root container; transient hubs are not tracked (callers own disposal).
-
Immutable after
AddNamedFileHubsreturns. No mutation methods onINamedFileHubs. -
Not keyed DI.
provider.GetRequiredKeyedService<IFileHub>("reports")throws — access is only throughINamedFileHubs.
INamedFileHubs hubs = new NamedFileHubsBuilder()
.Register("reports", new MemoryFileHub())
.Register("logs", new LocalFileHub(@"C:\logs"))
.Build();The core NamedFileHubsBuilder supports prebuilt instances only — no factories/lifetimes. Use AddNamedFileHubs for those.
if (builder.Environment.IsEnvironment("IntegrationTest"))
builder.Services.AddFileHub(_ => (IFileHub)new MemoryFileHub());
else
builder.Services.AddFileHub(_ => (IFileHub)new LocalFileHub(@"C:\data"));Consumer code depending on IFileHub doesn't change. More at Testing.
FileHub — unified file & directory API for .NET. Core is dependency-free; drivers are opt-in packages.