TCIS.Pluggable.Engine.Autofac 1.0.8

dotnet add package TCIS.Pluggable.Engine.Autofac --version 1.0.8
                    
NuGet\Install-Package TCIS.Pluggable.Engine.Autofac -Version 1.0.8
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TCIS.Pluggable.Engine.Autofac" Version="1.0.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TCIS.Pluggable.Engine.Autofac" Version="1.0.8" />
                    
Directory.Packages.props
<PackageReference Include="TCIS.Pluggable.Engine.Autofac" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TCIS.Pluggable.Engine.Autofac --version 1.0.8
                    
#r "nuget: TCIS.Pluggable.Engine.Autofac, 1.0.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TCIS.Pluggable.Engine.Autofac@1.0.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TCIS.Pluggable.Engine.Autofac&version=1.0.8
                    
Install as a Cake Addin
#tool nuget:?package=TCIS.Pluggable.Engine.Autofac&version=1.0.8
                    
Install as a Cake Tool

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:

  1. a test that builds the registrations on Microsoft DI with ValidateOnBuild = true, ValidateScopes = true — the registration check;
  2. a test that builds the host exactly as Program does (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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed