TCIS.Pluggable.Engine.Autofac
1.0.8-rc.9
See the version list below for details.
dotnet add package TCIS.Pluggable.Engine.Autofac --version 1.0.8-rc.9
NuGet\Install-Package TCIS.Pluggable.Engine.Autofac -Version 1.0.8-rc.9
<PackageReference Include="TCIS.Pluggable.Engine.Autofac" Version="1.0.8-rc.9" />
<PackageVersion Include="TCIS.Pluggable.Engine.Autofac" Version="1.0.8-rc.9" />
<PackageReference Include="TCIS.Pluggable.Engine.Autofac" />
paket add TCIS.Pluggable.Engine.Autofac --version 1.0.8-rc.9
#r "nuget: TCIS.Pluggable.Engine.Autofac, 1.0.8-rc.9"
#:package TCIS.Pluggable.Engine.Autofac@1.0.8-rc.9
#addin nuget:?package=TCIS.Pluggable.Engine.Autofac&version=1.0.8-rc.9&prerelease
#tool nuget:?package=TCIS.Pluggable.Engine.Autofac&version=1.0.8-rc.9&prerelease
TCIS.Pluggable.Engine.Autofac
Swaps the default DI container for Autofac and adds AOP logging to the Pluggable Engine: input arguments, execution time and exceptions are logged for boundary classes without a single logging statement in the business code.
Required for Template 3 (Pluggable). Optional — but recommended — for Templates 1 and 2.
Table of contents
| Section | Contents |
|---|---|
| 1 | Registration |
| 2 | What gets intercepted |
| 3 | Configuration |
| 4 | ⚠️ The configuration binding trap |
| 5 | Pitfalls |
1. Registration
dotnet add package TCIS.Pluggable.Engine.Autofac
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseTPluggableAutofac();
// or with explicit settings
builder.Host.UseTPluggableAutofac(
configSection: "TLogSettings:AopSettings",
configureOptions: o => o.TargetNamespaces.Add("Port.CatLai"));
A worker host (Host.CreateApplicationBuilder) has no IHostBuilder; use the IHostApplicationBuilder overload —
same parameters, same behaviour. The IHostBuilder overload is unchanged.
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.AddTSerilog();
builder.UseTPluggableAutofac(configureOptions: o => o.ExcludedSuffixes.Add("Rule")); // see section 3
builder.Services.AddTLogging(builder.Configuration);
builder.Services.AddPluggablePipeline(builder.Configuration, typeof(MyPlatformModule).Assembly);
One call does three things:
| # | Work |
|---|---|
| 1 | Replaces the DI container with Autofac (AutofacServiceProviderFactory) |
| 2 | Registers PluggableLoggingModule, which injects interceptors |
| 3 | Replaces IRealTypeResolver with AutofacRealTypeResolver so the engine sees through proxies to the real type |
Step 3 matters more than it looks: without it, discovery and metrics would report the proxy type name instead of your class, and step resolution by type would miss. The resolver is registered inside the Autofac container, so it wins over the engine's DefaultRealTypeResolver whether UseTPluggableAutofac is called before or after AddPluggablePipeline. (Before 30/10/2026 it went through IServiceCollection and the call order decided which one won — the default one winning made every rule "not seen by discovery" under RequireRuleOrder.)
2. What gets intercepted
PluggableLoggingModule decides in four steps:
| # | Rule |
|---|---|
| 1 | Early exclusion — namespace not in TargetNamespaces, or the name ends with an ExcludedSuffixes entry |
| 2 | By interface — implements IPipelineStep<> or IBusinessRule<> → always intercepted |
| 3 | By naming convention — the name ends with an IncludedSuffixes entry |
| 4 | Interception is attached through an Autofac 6 pipeline middleware at the Activation phase |
Rule 2 is the important one: every pipeline step and business rule is instrumented regardless of its name, because those are exactly the classes whose duration and failures the metrics are built on. Rule 1 still runs first, so an ExcludedSuffixes entry removes even a step or a rule.
The proxy is an interface proxy with target, which has two consequences:
| Registration | Result |
|---|---|
Resolved through an interface (AddScoped<IFoo, Foo>, keyed, multi-tenant dispatcher) |
Proxied |
Resolved as a concrete type (AddScoped<Foo>()) |
Not proxied, no error — the call is simply not logged. Register it behind an interface to log it |
Factory registration (AddScoped<IFoo>(sp => …), AddMultiTenantService) |
Suffixes match the interface name (IFoo), not the class name |
Code that resolves an interface and casts the result to its concrete class throws InvalidCastException under the proxy. Read
the real type with IRealTypeResolver, never with GetType() on an instance.
// Intercepted — implements IPipelineStep<>
[PlatformStep("Gate.ValidateContainer", executionOrder: 10)]
public sealed class CoreValidateContainerStep : IPipelineStep<IGateInContext> { … }
// Intercepted — name ends with "Service"
public sealed class BerthAllocationService : IBerthAllocationService { … }
// NOT intercepted — name ends with "Context"
public sealed class GateInContext : TPipelineContextBase<GateInResponse> { … }
3. Configuration
{
"TLogSettings": {
"AopSettings": {
"TargetNamespaces": [ "TCIS" ],
"ExcludedSuffixes": [ "Context", "Options", "Attribute" ],
"IncludedSuffixes": [ "Service", "QueryProvider" ]
}
}
}
| Option | Default | Purpose |
|---|---|---|
TargetNamespaces |
["TCIS"] |
Root namespaces considered at all. Anything outside is ignored immediately |
ExcludedSuffixes |
["Context", "Options", "Attribute"] |
Never intercept — avoids proxy overhead on configuration and context objects |
IncludedSuffixes |
["Service", "QueryProvider"] |
Boundary classes to intercept by naming convention |
Pipelines with many rules
Every intercepted call writes one record — successes included. A pipeline running 40 rules per item writes 40 rule records per item, and a worker that re-scans a waiting queue every few seconds writes them again on every pass. Three knobs, none of which changes the package defaults:
| Knob | Effect |
|---|---|
ExcludedSuffixes.Add("Rule") |
Rules are no longer proxied. Log at the step boundary; a rule's outcome belongs in the business log, keyed by rule |
IncludedSuffixes.Add("ContainerState") … |
Add the I/O ports (database, external API) — the boundary that is worth a record per call. Prefer long, exact suffixes over broad ones such as "State" |
TLogSettings:InvocationSuccessLevel = "DEBUG" |
Successful calls drop below the default minimum level and are not even built; failures (WARN/ERROR) and slow-call warnings are unchanged. Raise the level with Dynamic Log Level when investigating |
Declare these in code (configureOptions) rather than in appsettings, next to the reason — and see section 4 for why
appsettings lists append instead of replacing.
Testing a host that uses Autofac
AutofacServiceProviderFactory has no equivalent of ValidateOnBuild / ValidateScopes: a missing registration surfaces only
when resolved, and a singleton holding a scoped service is not reported — the scoped instance lives for the container's
lifetime. Keep both:
- a test that builds the registrations on Microsoft DI with
ValidateOnBuild = true, ValidateScopes = true— the registration check; - a test that builds the host exactly as
Programdoes (ideally through the same method), on Autofac — the check of what actually runs, proxies included.
4. ⚠️ The configuration binding trap
IConfiguration.Bind appends to a list that already holds defaults; it does not replace it.
{ "TLogSettings": { "AopSettings": { "TargetNamespaces": [ "Port.CatLai" ] } } }
// What you expect
["Port.CatLai"]
// What you actually get
["TCIS", "Port.CatLai"]
This surprises people in both directions: a namespace you meant to exclude stays included, and a list you meant to shrink grows instead. To end up with only your own value, clear the list in code:
builder.Host.UseTPluggableAutofac(configureOptions: o =>
{
o.TargetNamespaces.Clear();
o.TargetNamespaces.Add("Port.CatLai");
});
configureOptions runs after binding, so it always has the last word.
This behaviour is pinned by R4_AopOptionsBindingTests — it is a property of Microsoft.Extensions.Configuration, not something this package can change.
5. Pitfalls
| # | Pitfall | Consequence |
|---|---|---|
| 1 | Expecting Bind to replace a list |
Defaults silently remain — see section 4 |
| 2 | Adding an interception target that is resolved as a concrete class | Silently not intercepted — see section 2 |
| 3 | Registering services before UseTPluggableAutofac in a way that bypasses the container factory |
Those registrations are not intercepted |
| 4 | Intercepting hot paths by adding a broad suffix such as "Entity" |
Proxy overhead on objects created thousands of times per request |
| 5 | Assuming interception implies transaction or retry behaviour | It only logs — it changes no semantics |
Captive dependencies are avoided by design: the interceptor is
ResolveNamed-ed inside the middleware, once per resolution, rather than being held at module scope. Do not "optimise" that by caching the interceptor — it would capture a scoped graph into a singleton.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Autofac (>= 8.1.1)
- Autofac.Extensions.DependencyInjection (>= 10.0.0)
- Autofac.Extras.DynamicProxy (>= 7.1.0)
- Castle.Core.AsyncInterceptor (>= 2.1.0)
- Elastic.CommonSchema (>= 8.11.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- System.Diagnostics.DiagnosticSource (>= 10.0.0)
- System.Text.Encodings.Web (>= 9.0.0)
- System.Text.Json (>= 9.0.0)
- System.Threading.Channels (>= 8.0.0)
- TCIS.Logging.Autofac (>= 1.0.8-rc.9)
- TCIS.Pluggable.Engine (>= 1.0.8-rc.9)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on TCIS.Pluggable.Engine.Autofac:
| Package | Downloads |
|---|---|
|
TCIS.Logging.Hosting
One-call TCIS application logging composition, startup lifecycle and downstream failure attribution. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.8 | 0 | 11/3/2026 |
| 1.0.8-rc.11 | 48 | 10/30/2026 |
| 1.0.8-rc.10 | 46 | 10/30/2026 |
| 1.0.8-rc.9 | 46 | 10/30/2026 |
| 1.0.8-rc.8 | 47 | 10/29/2026 |
| 1.0.8-rc.7 | 50 | 10/27/2026 |
| 1.0.8-rc.6 | 57 | 10/27/2026 |
| 1.0.8-rc.5 | 67 | 10/23/2026 |
| 1.0.8-rc.4 | 66 | 10/23/2026 |
| 1.0.8-rc.3 | 62 | 10/23/2026 |
| 1.0.8-rc.2 | 59 | 10/22/2026 |
| 1.0.8-rc.1 | 62 | 10/22/2026 |
| 1.0.7 | 101 | 10/22/2026 |
| 1.0.6 | 108 | 10/20/2026 |
| 1.0.6-rc.1 | 57 | 10/20/2026 |
| 1.0.5 | 106 | 10/19/2026 |
| 1.0.4 | 103 | 10/13/2026 |
| 1.0.3 | 103 | 10/13/2026 |
| 1.0.2 | 101 | 10/12/2026 |
| 1.0.2-rc.1 | 58 | 10/12/2026 |