BitzOrcas.Infrastructure provides the cross-cutting infrastructure services that every host and module depends on — audit logging, clock, file storage, seed data, and observability primitives. These are ORM-agnostic by design.
Audit pipeline
BitzOrcas uses a channel-based async audit pipeline that decouples audit collection from audit storage:
Request → RequestAuditMiddleware → IAuditLogger │ ▼ ChannelAuditQueue (Channel<T>) │ ▼ Background AuditWriter │ ┌─────────┴─────────┐ ▼ ▼ IAuditLogStore IRequestAuditSink (DB storage) (email/webhook, etc.)Key interfaces
| Interface | Purpose |
|---|---|
IAuditLogger | Enqueue audit entries (fire-and-forget) |
IAuditLogStore | Persist audit records to database |
IRequestAuditSink | Extension point for additional audit consumers |
IAuditTableInitializer | Create audit tables during schema init |
IAuditQueryPort | Query audit logs (read-only) |
IAuditRetentionPort | Execute retention/cleanup policies |
Default behavior
- API Shell mode (no DB): Null sinks — audit entries are silently dropped
- SqlSugar path:
SqlSugarAuditLogStorepersists to 7 sharded audit tables - Background writer: Processes entries from
Channel<T>without blocking request handling
Clock abstraction
public interface IAppClock{ DateTimeOffset UtcNow { get; }}SystemClock— production implementation, defaults to Shanghai UTC+8FixedClock— for testing deterministic time-dependent logic- Configurable via
Clock:Kind(LocalorUtc)
Seed framework
BitzOrcas uses a CSV-based seed framework instead of EF Core migrations or C# seed methods:
Architecture
| Component | Purpose |
|---|---|
ISeedStep | Unit-of-work interface for a single seed step |
ISeedRunner | Orchestrates all registered steps in dependency order |
CsvSeedReader | Reads CSV files with header mapping |
CsvSeedStepBase<T> | Generic base for SqlSugar CSV seed steps |
SeedRunReport | Execution results (created/updated/skipped counts) |
Seed data organization
Seeders/Assets/├── Identity/│ ├── platform_tenant.csv│ ├── sys_role.csv│ ├── sys_role_type.csv│ ├── sys_module.csv│ ├── sys_permission.csv│ └── sys_role_module_permission.csv└── MasterData/ ├── sys_country.csv ├── sys_language.csv ├── sys_general_code_group.csv ├── sys_general_code.csv ├── sys_general_code_text.csv ├── sys_exchange_rate.csv ├── sys_industry_setting.csv └── sys_public_holiday.csvHow seeding works
CsvSeedStepBase<T>reads the CSV, maps columns to entity properties viaCsvSeedReader- Uses SqlSugar’s
StorageableAPI for upsert semantics — no duplicate errors SeedRunnerexecutes all steps, produces aSeedRunReport- Triggered via
--init-schema --seed-demoor--seed-onlyflags
File storage
BitzOrcas.Infrastructure.Storage provides a local file storage implementation:
public interface IFileStorage{ Task<string> SaveAsync(Stream stream, string contentType, string extension, CancellationToken ct = default); Task<Stream?> ReadAsync(string path, CancellationToken ct = default); Task DeleteAsync(string path, CancellationToken ct = default);}LocalFileStorage— saves files to a configurable directory on disk- Configurable via
Storage:BasePathsetting - Files module (
FileAssetService) uses this abstraction
Current user access
public interface ICurrentUser{ Guid? UserId { get; } string? TenantId { get; } string? UserName { get; } // ... additional claims}HttpContextCurrentUser(API Host) — reads from JWT claimsAnonymousCurrentUser(JobHost) — no HTTP context availableICurrentUserAccessor— enables run-as scoping for delegation/job contexts
Assembly reference
BitzOrcas.Infrastructure ├── Auditing/ → IAuditLogger, ChannelAuditQueue, AuditEntryNormalizer ├── Clocking/ → SystemClock, FixedClock, IAppClock ├── Storage/ → LocalFileStorage, IFileStorage, LocalFileStorageOptions ├── Seeders/ → ISeedStep, ISeedRunner, CsvSeedReader, SeedRunner ├── Schema/ → IPersistenceSchemaInitializer ├── Users/ → CurrentUserAccessor, AnonymousCurrentUser ├── Http/ → ExternalRequestLoggingHandler └── Observability/ → ObservabilityExtensions (SqlSugar ActivitySource)