Pragmatic.Composition
Source-generated application composition and dependency injection for .NET 10. Declare modules, services, and startup steps — the generator writes all the wiring.
The Problem
Section titled “The Problem”Every .NET application accumulates the same startup ceremony: register services one by one, configure middleware in the right order, wire database contexts, and keep it all consistent across modules. With 50+ services, Program.cs becomes a wall of services.AddScoped<>() calls that no one wants to maintain.
// Without Pragmatic: 80+ lines of mechanical wiringvar builder = WebApplication.CreateBuilder(args);builder.Services.AddScoped<IOrderService, OrderService>();builder.Services.AddScoped<IProductService, ProductService>();builder.Services.AddScoped<IInventoryService, InventoryService>();// ... 50 more services, growing with every new classbuilder.Services.AddDbContext<AppDbContext>(o => o.UseSqlServer(config.GetConnectionString("App")));builder.Services.AddDbContext<BillingDbContext>(o => o.UseSqlServer(config.GetConnectionString("Billing")));builder.Services.AddAuthentication("Bearer").AddJwtBearer(o => { /* ... */ });var app = builder.Build();app.UseResponseCompression();app.UseRouting();app.UseCors();app.UseAuthentication();app.UseAuthorization();// Forget one line, the app silently breaksThe Solution
Section titled “The Solution”With Pragmatic.Composition, you declare WHAT your application is. The source generator handles HOW it starts up.
// With Pragmatic: declare the shape, the SG generates the restawait PragmaticApp.RunAsync(args, app =>{ if (app.Environment.IsDevelopment()) app.UseDatabaseEnsureCreated(); app.UseAuthentication<NoOpAuthenticationHandler>("PragmaticDefault");}).ConfigureAwait(false);// Module topology -- one class per bounded context[Module][Include<OrdersModule, AppDatabase>][Include<BillingModule, FinancialDatabase>]public sealed class MyAppModule;
// Services register themselves[Service]public class OrderService(IOrderRepository repository) : IOrderService { }The SG produces the complete host startup: infrastructure auto-registration, ordered startup steps, database initialization, endpoint mapping, telemetry, and maintenance mode — all at compile time, zero reflection.
Overview
Section titled “Overview”Pragmatic.Composition replaces manual services.AddScoped<>() registration with source-generated DI. Mark classes with [Service], order decorators with [Decorator], organize modules with [Module], and define application lifecycle with [StartupStep] and IPragmaticBuilder. The generator emits all registrations as compile-time code — no reflection, no assembly scanning at runtime.
The module ships as two packages:
| Package | Role |
|---|---|
Pragmatic.Composition | Meta-package (references Abstractions + Host) |
Pragmatic.Composition.Host | ASP.NET Core hosting runtime: PragmaticApp, IStartupStep, assembly scanning, telemetry, remote boundaries, maintenance mode |
Attributes ([Service], [Module], [StartupStep], etc.) live in Pragmatic.Abstractions so domain modules can use them without referencing ASP.NET Core.
At startup, one call (PragmaticApp.RunAsync()) wires everything. Cross-assembly discovery works through [PragmaticMetadata] assembly attributes — libraries declare what they register, the host aggregates automatically.
Installation
Section titled “Installation”dotnet add package Pragmatic.CompositionAdd the source generator as a project reference:
<ProjectReference Include="..\Pragmatic.SourceGenerator\src\Pragmatic.SourceGenerator\Pragmatic.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />Quick Start
Section titled “Quick Start”1. Define Your Module Topology
Section titled “1. Define Your Module Topology”Each domain boundary declares a module class. Libraries use [IncludeModule<T>] for cross-boundary dependencies:
[Module(Name = "Showcase.Booking", Version = "1.0.0", Description = "Reservation and guest management")][IncludeModule<CatalogModule>]public sealed class BookingModule;The host declares the deployment topology — which modules are included and which database each uses:
[Module][Include<AccountsModule, ShowcaseAppDatabase>][Include<BillingModule, ShowcaseFinancialDatabase>][Include<BookingModule, ShowcaseAppDatabase>][Include<CatalogModule, ShowcaseAppDatabase>][NeedsStep<InternationalizationStep>][NeedsStep<RoutingStep>]public sealed class ShowcaseHostModule;2. Configure the Host (Program.cs)
Section titled “2. Configure the Host (Program.cs)”PragmaticApp.RunAsync is the entry point. The source generator replaces the stub implementation with a fully wired host based on discovered modules:
await PragmaticApp.RunAsync(args, app =>{ // Database -- create tables automatically in development if (app.Environment.IsDevelopment()) app.UseDatabaseEnsureCreated();
// Multi-tenancy -- resolve tenant from HTTP header app.UseMultiTenancy(mt => mt.UseHeader());
// Authentication -- config-driven: JWT for production, NoOp for development var jwtKey = app.Configuration["Jwt:Key"]; if (!string.IsNullOrEmpty(jwtKey)) app.UseJwtAuthentication(jwt => { jwt.SigningKey = jwtKey; /* ... */ }); else app.UseAuthentication<NoOpAuthenticationHandler>("PragmaticDefault");
// Authorization -- compose application roles from module definitions app.UseAuthorization(authz => { authz.DefaultPolicy = DefaultEndpointPolicy.RequireAuthenticated; authz.MapRole<ShowcaseAdmin>(); authz.MapRole<BookingManagerRole>(r => r .IncludeDefinition<BookingOperator>() .IncludeDefinition<CatalogReader>()); });
// Logging -- Console + daily rolling file app.UseLogging(log => { log.AddConsole(PragmaticConsoleConfiguration.ForDevelopment()); log.AddFile("logs/app-{Date}.log", config => { /* ... */ }); });
// File storage app.UseStorage(sp => { var basePath = Path.Combine(app.Environment.ContentRootPath, "wwwroot"); return new LocalDiskFileStorage(basePath, sp.GetRequiredService<ILogger<LocalDiskFileStorage>>()); });}).ConfigureAwait(false);3. Add Business Wiring (IStartupStep)
Section titled “3. Add Business Wiring (IStartupStep)”Application-specific services and middleware are configured via IStartupStep:
[StartupStep][RequiresConfig("ConnectionStrings:App")]public class ShowcaseStartupStep : IStartupStep{ public int Order => 60;
public void ConfigureServices( IServiceCollection services, IConfiguration configuration, IHostEnvironment environment) { services.AddShowcaseConfiguration(configuration); services.AddShowcaseQueryFilters(); services.AddOpenApi(options => options.AddResultTypeSupport()); }
public void ConfigurePipeline(IApplicationBuilder app) { app.UseMiddleware<TenantResolutionMiddleware>(); app.UseAuthentication(); app.UseAuthorization(); }}3-Tier Configuration Model
Section titled “3-Tier Configuration Model”Pragmatic.Composition organizes startup into three tiers, each with a distinct responsibility:
| Tier | Where | What | Who |
|---|---|---|---|
| 1. Topology | [Module], [Include], [BelongsTo] | Compile-time structure, module dependencies | Source generator (automatic) |
| 2. Module Strategy | Program.cs via IPragmaticBuilder | Infrastructure choices: auth, storage, logging, multi-tenancy | Developer (explicit) |
| 3. Business Wiring | IStartupStep | App services, query filters, middleware, OpenAPI, feature flags | Developer (explicit) |
Topology (compile-time) --> Module Strategy (Program.cs) --> Business Wiring (IStartupStep) SG auto-detect IPragmaticBuilder IStartupStepExecution order: The SG registers infrastructure defaults first, the IPragmaticBuilder callback overrides them (DI last-registration-wins), then IStartupStep.ConfigureServices runs in Order sequence.
IPragmaticBuilder
Section titled “IPragmaticBuilder”Defined in Pragmatic.Abstractions (namespace Pragmatic.Composition):
public interface IPragmaticBuilder{ IServiceCollection Services { get; } IConfiguration Configuration { get; } IHostEnvironment Environment { get; }}Each infrastructure module contributes Use*() extension methods on IPragmaticBuilder. IntelliSense shows only the modules referenced by the host project.
Available Use*() Extension Methods
Section titled “Available Use*() Extension Methods”| Method | Package | What It Configures |
|---|---|---|
UseDatabaseEnsureCreated() | Composition.Host | Schema creation on startup (development) |
UseDatabaseMigrate() | Composition.Host | Run pending EF Core migrations on startup |
UseAuthentication<THandler>(scheme) | Identity.AspNetCore | Authentication handler + scheme |
UseJwtAuthentication(jwt => { ... }) | Identity.Local.Jwt | JWT Bearer with signing key, issuer, expiry |
UseAuthorization(authz => { ... }) | Authorization | Roles, permissions, groups, policies, ABAC |
UseMultiTenancy(mt => { ... }) | MultiTenancy.AspNetCore | Tenant resolution strategy |
UseLogging(log => { ... }) | Logging | Console + file loggers |
UseStorage(sp => new LocalDiskFileStorage(...)) | Storage | File storage provider |
The concrete implementation is PragmaticBuilder in Pragmatic.Composition.Host, which adds a PragmaticOptions property for cross-cutting hosting options (telemetry, maintenance mode, database initialization).
Auto-Registered Infrastructure Modules
Section titled “Auto-Registered Infrastructure Modules”When a Pragmatic infrastructure package is referenced in the .csproj, the SG auto-registers its default. Override via IPragmaticBuilder:
| Module | Default Registration | Override Method |
|---|---|---|
| Temporal | SystemClock as IClock (Singleton) | — |
| Resilience | AddPragmaticResilience() + bind ResilienceOptions | — |
| Caching | AddHybridCache() + AddPragmaticCaching() | — |
| I18n | AddPragmaticI18n() | — |
| Identity | AddPragmaticAuthorization() | UseAuthentication<T>() |
| MultiTenancy | AddPragmaticMultiTenancy() (SingleTenant default) | UseMultiTenancy() |
| FeatureFlags | AddPragmaticFeatureFlags() | — |
| Discovery | AddPragmaticDiscovery() | — |
| Authorization | AddPragmaticAuthorization() | UseAuthorization() |
Override behavior: SG registers defaults FIRST, then the
IPragmaticBuildercallback runs. DI uses last-registration-wins, soUse*()calls override the SG defaults. Finally,IStartupStep.ConfigureServices()runs for business-level wiring.Execution order: SG defaults →
IPragmaticBuildercallback →IStartupStep.ConfigureServices()→app.Build()→IStartupStep.ConfigurePipeline()→MapAllEndpoints()
Feature Catalog
Section titled “Feature Catalog”Service Registration
Section titled “Service Registration”Mark classes with [Service] for automatic DI registration at compile time:
// Default: scoped, registered as first implemented interface[Service]public class OrderService : IOrderService { /* ... */ }
// Explicit interface (generic attribute)[Service<IPaymentProvider>(Key = "stripe")]public class StripePaymentProvider : IPaymentProvider { /* ... */ }
// Register as self (concrete type)[Service(AsSelf = true, Lifetime = ServiceLifetime.Singleton)]public class CacheWarmupService { /* ... */ }ServiceAttribute properties:
| Property | Type | Default | Description |
|---|---|---|---|
Lifetime | ServiceLifetime | Scoped | Singleton, Scoped, or Transient |
As | Type? | null | Register as this type (prefer ServiceAttribute<T>) |
AsSelf | bool | false | Register as concrete type |
Key | string? | null | Keyed service registration (.NET 8+) |
Decorators
Section titled “Decorators”Decorators wrap existing services. They are applied in ascending Order (lower = closer to real service, higher = outermost):
[Decorator(Order = 1)]public class LoggingReservationPricingService( IReservationPricingService inner, ILogger<LoggingReservationPricingService> logger) : IReservationPricingService{ // Wraps inner with logging}Call flow: Outer (Order=2) --> Inner (Order=1) --> RealServiceThe Decorate<TService, TDecorator>() extension method on IServiceCollection is also available for manual decoration in IStartupStep.ConfigureServices.
Service Factories
Section titled “Service Factories”For services that need custom construction logic:
[ServiceFactory]public class ConnectionFactories{ [Factory(Lifetime = ServiceLifetime.Singleton)] public IDbConnection CreateConnection(IConfiguration config) { var connStr = config.GetConnectionString("Default"); return new SqlConnection(connStr); }}[ServiceFactory] marks the container class (registered as singleton). [Factory] marks individual factory methods whose return type becomes the registered service type.
Property Injection
Section titled “Property Injection”For optional dependencies or post-construction initialization:
[Service]public class NotificationService : INotificationService{ [Inject] public IEmailSender? EmailSender { get; set; }
[Inject(Required = true, Key = "priority")] public IMessageQueue Queue { get; set; } = null!;}InjectAttribute properties:
| Property | Type | Default | Description |
|---|---|---|---|
Required | bool | false | Throw if service not available |
Key | string? | null | Keyed service resolution |
Modules and Topology
Section titled “Modules and Topology”[Module] declares a boundary/module. Applied to an empty sealed class:
[Module(Name = "Showcase.Billing", Version = "1.0.0", Description = "Invoice and payment processing")][IncludeModule<BookingModule>]public sealed class BillingModule;[IncludeModule<T>] declares a library-level dependency on another module.
[Include<TModule>] and [Include<TModule, TDatabase>] are host-level attributes that wire modules into the deployment topology:
[Module][Include<BookingModule, ShowcaseAppDatabase>][Include<BillingModule, ShowcaseFinancialDatabase>]public sealed class ShowcaseHostModule;The three Include overloads:
| Attribute | Purpose |
|---|---|
[Include<TModule>] | Include module without database |
[Include<TModule, TDatabase>] | Include module wired to a database (DbContext name auto-derived) |
[Include<TModule, TDatabase, TDbContext>] | Include module with explicit DbContext class name |
Databases
Section titled “Databases”Declare physical databases by deriving from PragmaticDatabase:
[PragmaticDatabase(Provider = DatabaseProvider.SqlServer, ConfigKey = "ConnectionStrings:App")]public sealed class ShowcaseAppDatabase : PragmaticDatabase { }Supported providers: SqlServer, PostgreSql, SQLite, MySql, InMemory.
Startup Steps
Section titled “Startup Steps”IStartupStep defines the two-phase startup lifecycle:
public interface IStartupStep{ int Order => 0; void ConfigureServices(IServiceCollection services, IConfiguration configuration, IHostEnvironment environment) { } void ConfigurePipeline(IApplicationBuilder app) { }}Mark implementations with [StartupStep] for auto-discovery. Order determines execution sequence:
| Range | Purpose | Examples |
|---|---|---|
| 0-99 | Infrastructure | ResponseCompressionStep (25), RoutingStep (50), CorsStep (75) |
| 100-499 | Module steps | Authentication, authorization, i18n |
| 500+ | Application steps | Business services, OpenAPI, custom middleware |
Built-in steps provided by Pragmatic.Composition.Host:
| Step | Order | What It Does |
|---|---|---|
ResponseCompressionStep | 25 | app.UseResponseCompression() |
RoutingStep | 50 | app.UseRouting() |
CorsStep | 75 | app.UseCors() |
Use [NeedsStep<T>] on a module to declare that it requires a specific step in the pipeline. The SG deduplicates and orders all needed steps automatically.
[RequiresConfig("path")] on a startup step declares required configuration keys. The host validates them at startup (fail-fast before the application starts).
Packages
Section titled “Packages”Packages are pre-built collections of entities, actions, and services that fuse into a host module:
// Package definitionpublic class LocalIdentityPackage : IPackageDefinition{ public static string PackageName => "Identity.Local"; public static string? RoutePrefix => "identity/local"; public static string? Description => "Local username/password identity provider";}
// Import in module[UsePackage<LocalIdentityPackage>]public sealed class AccountsModule;IPackageDefinition requires: PackageName, RoutePrefix, Description. Optional: RequiredTypeNames for compile-time dependency validation.
Assembly Scanning
Section titled “Assembly Scanning”For convention-based registration without attributes, use the fluent scanning API:
services.Scan(scan => scan .FromAssemblyOf<OrderService>() .AddClasses(filter => filter.AssignableTo<ICommandHandler>()) .AsImplementedInterfaces() .WithScopedLifetime());Assembly sources:
| Method | Description |
|---|---|
FromAssemblyOf<T>() | Assembly containing type T |
FromAssemblies(params Assembly[]) | Explicit assembly list |
FromAssembliesMatching(string pattern) | Glob pattern on DLL file names |
FromCallingAssembly() | Calling assembly |
FromEntryAssembly() | Entry assembly |
FromDependencyContext(predicate?) | From DependencyContext.Default |
FromDependencyContext(string prefix) | Libraries whose name starts with prefix |
Type filters (AddClasses(filter => ...)):**
| Method | Description |
|---|---|
AssignableTo<T>() / AssignableTo(Type) | Implements interface or extends class (supports open generics) |
WithAttribute<T>() | Has specific attribute |
InNamespace(string) / InNamespaceOf<T>() | In specific namespace |
Where(Func<Type, bool>) / NotWhere(...) | Custom predicate |
Registration modes (AsImplementedInterfaces(), etc.):**
| Method | Description |
|---|---|
AsImplementedInterfaces() | All implemented interfaces |
AsSelf() | Concrete type only |
AsSelfWithInterfaces() | Concrete type + all interfaces |
As<T>() / As(Type) / As(Func<Type, Type>) | Specific type |
AsMatchingInterface() | Interface named I{ClassName} |
Registration strategies control duplicate handling:
| Strategy | Behavior |
|---|---|
Append (default) | Always adds, even if duplicate |
Skip | Skip if service type already registered |
Replace | Remove existing, add new |
Throw | Throw InvalidOperationException on duplicate |
Remote Boundaries
Section titled “Remote Boundaries”For distributed deployments, [RemoteBoundary<TModule>] replaces a local module with HTTP-based invokers:
// Distributed host: Billing lives on a separate service[Module][Include<BookingModule, ShowcaseAppDatabase>][Include<CatalogModule, ShowcaseAppDatabase>][RemoteBoundary<BillingModule>]public sealed class ShowcaseDistributedModule;The SG generates HttpActionInvoker<TAction, TReturn> for each action in the remote module. Actions are serialized as JSON, POSTed to /_pragmatic/invoke, and deserialized back.
Configuration (appsettings.json):
{ "Pragmatic": { "RemoteBoundaries": { "Billing": { "BaseUrl": "https://billing-service:5001" } } }}Runtime types in Pragmatic.Composition.Host/Remote/:
| Type | Role |
|---|---|
HttpActionInvoker<TAction, TReturn> | Sends action via HTTP, returns Result<TReturn, IError> |
HttpVoidActionInvoker<TAction> | Same for void actions, returns VoidResult<IError> |
PragmaticInvokeDispatcher | Server-side: resolves invoker from DI, dispatches action |
PragmaticInvokeRequest | Wire format: { ActionType, Payload } |
PragmaticInvokeResponse | Wire format: { IsSuccess, Value?, Error? } |
RemoteError | Error type for failed remote invocations |
Cross-Assembly Discovery
Section titled “Cross-Assembly Discovery”Each source generator emits [PragmaticMetadata] assembly-level attributes with category-specific JSON data. The host generator reads these from referenced assemblies and generates aggregated registration code.
MetadataCategory enum values: DI, Mapping, Actions, Startup, Validation, Endpoints, HealthChecks, Identifiers, Module, Translations, Persistence, EventHandlers, HostTopology, Caching, Configuration.
Telemetry
Section titled “Telemetry”PragmaticTelemetry.AddPragmaticTelemetry() configures OpenTelemetry with sensible defaults, called automatically by the generated PragmaticApp.RunAsync:
- Tracing: ASP.NET Core, HttpClient, EF Core instrumentation. All Pragmatic ActivitySources registered.
- Metrics: ASP.NET Core metrics. All Pragmatic Meters registered.
- Logging: OTel logging bridge with formatted messages and scopes.
- Sampling: AlwaysOn in Development, ratio-based (default 10%) in production.
- Exporters: Console in Development, OTLP when
TelemetryOptions.UseOtlpExporter = true.
Maintenance Mode
Section titled “Maintenance Mode”When the application fails to start, MaintenanceMode provides diagnostic endpoints instead of crashing:
| Endpoint | Response |
|---|---|
/health | 503 with status, error type, message |
/maintenance | 503 with full error details, stack trace (dev only), suggestions |
| Any other path | 503 with pointer to /health and /maintenance |
Configure via PragmaticOptions.MaintenanceMode:
public sealed class MaintenanceModeOptions{ public bool EnableOnStartupFailure { get; set; } = true; public string HealthPath { get; set; } = "/health"; public string MaintenancePath { get; set; } = "/maintenance"; public bool? IncludeStackTrace { get; set; } // auto in Development}Database Initialization
Section titled “Database Initialization”Extension methods on IPragmaticBuilder for development convenience:
| Method | Description |
|---|---|
UseDatabaseEnsureCreated() | Calls EF Core EnsureCreatedAsync on startup (dev only) |
UseDatabaseMigrate() | Applies pending EF Core migrations on startup (production) |
PragmaticApp Entry Points
Section titled “PragmaticApp Entry Points”PragmaticApp is a static class with stub methods. The source generator replaces the implementation based on discovered [PragmaticMetadata] attributes:
| Method | Description |
|---|---|
RunAsync(string[] args, Action<IPragmaticBuilder>? configure) | Web application host |
RunWorkerAsync(string[] args, Action<IPragmaticBuilder>? configure) | Background worker host |
RunConsoleAsync(string[] args, Action<IPragmaticBuilder>? configure) | Console application host |
Documentation
Section titled “Documentation”Detailed guides in docs/:
| Guide | What You’ll Learn |
|---|---|
| Architecture and Concepts | 3-Tier model, module system, IPragmaticBuilder, what gets generated |
| Getting Started | Minimal host setup, adding modules step by step |
| Startup Pipeline | IStartupStep ordering, ConfigureServices vs ConfigurePipeline |
| Service Registration | [Service], [Decorator], [ServiceFactory], keyed services |
| Remote Boundaries | [RemoteBoundary], HttpActionInvoker, distributed deployment |
| Common Mistakes | Patterns to avoid and how to fix them |
| Troubleshooting | Problem/solution guide, diagnostics reference, FAQ |
Cross-Module Integration
Section titled “Cross-Module Integration”| With Module | Integration |
|---|---|
| Pragmatic.Actions | AddMyAppActions() registers all action invokers |
| Pragmatic.Validation | AddGeneratedValidators() registers all [Validator] types |
| Pragmatic.Persistence | AddBillingRepositories() + AddBillingDbContext() in pipeline step |
| Pragmatic.Endpoints | MapPragmaticEndpoints() in ConfigurePipeline() |
Diagnostics
Section titled “Diagnostics”| ID | Severity | Category | Description |
|---|---|---|---|
| PRAG1050 | Error | Package | Duplicate [UsePackage<T>] on module |
| PRAG1600 | Error | Topology | Module requires unavailable middleware |
| PRAG1601 | Error | Topology | Module dependency not declared |
| PRAG1602 | Error | Topology | Circular dependency detected |
| PRAG1605 | Warning | Topology | [Service] without Pragmatic.Composition reference |
| PRAG1606 | Error | Topology | [Decorator] requires Pragmatic.Composition reference |
| PRAG1610 | Error | Schema | Incompatible metadata schema version |
| PRAG1611 | Warning | Schema | Newer metadata schema version than supported |
| PRAG1612 | Info | Schema | Legacy metadata schema version (backward compat) |
| PRAG1620 | Error | Injection | [Inject] references unregistered service |
| PRAG1621 | Error | Injection | [Inject] creates circular reference |
| PRAG1630 | Error | Startup | [StartupStep] must implement IStartupStep |
| PRAG1631 | Error | Startup | [StartupStep] must be on a class |
| PRAG1632 | Error | Startup | [NeedsStep<T>] references unavailable step type |
| PRAG1640 | Error | Service | [Service] requires a class type |
| PRAG1641 | Warning | Service | Dependency not registered |
| PRAG1642 | Warning | Service | Singleton depends on scoped service (captive dependency) |
| PRAG1643 | Warning | Service | No interface found for service |
| PRAG1644 | Info | Service | Service registered |
| PRAG1645 | Error | Service | Abstract class cannot be a service |
| PRAG1646 | Warning | Service | Keyed services require .NET 8+ |
| PRAG1651 | Warning | Database | Boundary has no database configured |
| PRAG1652 | Error | Database | DbContext name collision across different databases |
| PRAG1660 | Error | Decorator | Decorator must implement at least one interface |
| PRAG1661 | Error | Decorator | Decorator missing inner service constructor parameter |
| PRAG1670 | Error | Events | [EventHandler] on class not implementing IDomainEventHandler<T> |
| PRAG1680 | Warning | Package | [ExposeEndpoint<T>] references non-package action |
| PRAG1685 | Error | Remote | [RemoteBoundary<T>] and [Include<T>] on same module |
| PRAG1686 | Warning | Remote | [RemoteBoundary<T>] module has no actions |
| PRAG1687 | Info | Remote | [RemoteBoundary<T>] base URL not configured |
| PRAG1690 | Info | Discovery | Discovered Pragmatic modules count |
| PRAG1691 | Info | Discovery | Module composition info |
| PRAG1693 | Info | Discovery | No [Service] or [Decorator] registrations found |
| PRAG1694 | Info | Discovery | No [StartupStep] registrations found |
Samples
Section titled “Samples”See samples/Pragmatic.Composition.Samples/ for 3 conceptual scenarios: three-tier model (Topology → Strategy → Business), IStartupStep (ordering, ConfigureServices, ConfigurePipeline), and SG output (Host.Entry, Host.Services, auto-registration).
Requirements
Section titled “Requirements”- .NET 10.0+
Pragmatic.SourceGeneratoranalyzer
License
Section titled “License”Part of the Pragmatic.Design ecosystem.