autofac / autofac/Autofac.Extras.DynamicProxy

Support interceptors on decorator registrations

Open
#66 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
C#
Stars
108
Forks
32
PR merge metrics
No merged PRs in 30d

Description

### Summary

Allow a decorator registration to be intercepted using the same fluent syntax we already use for normal registrations:

```csharp
builder.RegisterType();

// The decorator gets intercepted, using familiar chained syntax.
builder
.RegisterDecorator()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(StringMethodInterceptor));

// The decorated type can be intercepted independently (existing syntax).
builder
.RegisterType()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(SomeOtherInterceptor))
.As();
```

### Background / motivation

Since Autofac v5, the resolve pipeline changed such that an interceptor attached to a *service* registration wraps the innermost (decorated) instance, not the outermost decorator. This is discussed at length in autofac/Autofac#1416. We are **not** proposing to change that order-of-operations behavior. Instead we want to let people **attach interception directly to the decorator registration**, so the interception point is explicit and reads naturally ("the decorator is intercepted by X") rather than relying on interceptors "magically" moving during decoration.

PR #55 attempted this with a standalone `builder.EnableInterfaceInterceptors()` call disconnected from the decorator registration. That syntax was rejected in review as confusing/inconsistent — it isn't clear what is intercepting what, or how it relates to `RegisterType` interception.

### Why this works mechanically

Tracing the core resolve pipeline: when `DecoratorMiddleware` resolves the decorator, it does so via a new `ResolveRequest` that preserves the **decorator's own registration**, and `RegistrationPipelineInvokeMiddleware` invokes `context.Registration.ResolvePipeline`. Interface interception attaches its middleware at the `Activation` phase on the registration pipeline. **So if interception middleware is present on the decorator registration's pipeline, it executes and wraps the (outermost) decorator instance.** No core runtime change is needed — only a way to configure that pipeline.

### The blocker (requires a core Autofac change)

The modern `RegisterDecorator()` returns `void` and builds + snapshots the decorator registration internally (`CreateRegistration()` clones the pipeline). There is no `IRegistrationBuilder` to chain `.EnableInterfaceInterceptors()` onto, and the pipeline is frozen by the time the method returns.

This needs a core change so decorator registration returns a configurable builder. The plan for that is captured in autofac/Autofac#1416. Once core exposes a decorator builder with `ConfigurePipeline` (and metadata for `InterceptedBy`), the work in *this* repo is small, since `EnableInterfaceInterceptors`/`InterceptedBy` are already extension methods.

### Scope & open questions for the implementation

- **Closed-type decorators first.** Open-generic decorators (`RegisterGenericDecorator`) build the closed decorator registration lazily at resolve time (`OpenGenericDecoratorMiddlewareSource`), so there's no registration-time builder to configure. Recommend deferring open-generic support to a follow-up.
- Feature parity to aim for (from the PR #55 review):
- `ProxyGenerationOptions` support on the decorator
- `[Intercept]` attribute selection on the decorator type
- A *different* interceptor for the decorator vs. the decorated type (good acceptance test)
- Sensible behavior for class-vs-interface interception combinations
- **Reusable cleanup regardless of this feature:** extract the duplicated proxy-application logic out of `RegistrationExtensions` into a shared `ProxyHelpers` (as PR #55 did). This can land independently.
- Process note: new tests should use **NSubstitute**, not Moq.

### Dependencies

Blocked on the core Autofac change tracked in autofac/Autofac#1416, which is a breaking change and must ship in a major core release. This issue tracks the downstream feature; PR #55 is the original prototype.

Contributor guide

Open the contributing guide

Research direction

Start with autofac/Autofac#1416 to confirm the required core decorator builder, then inspect RegistrationExtensions and the existing proxy-application logic. Use PR #55 and the closed-type RegisterDecorator path as entry points; done means decorator registrations can chain interception configuration, with separate decorator and decorated-type interceptors covered by NSubstitute tests. Open-generic support is deferred.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.