Skip to content

EnableClassInterceptors does not take into account enum KeyFilter parameters in the constructor #56

Open

Description

@elgatov

Describe the Bug

When using EnableClassInterceptors() and registering a class with WithAttributeFiltering(), parameters decorated with KeyFilter will not have their filter taken into account when trying to intercept the class

Steps to Reproduce

using Autofac;
using Autofac.Extras.DynamicProxy;
using Autofac.Features.AttributeFilters;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Pruebas
{
    [TestClass]
    public class TestClassWithKeyedAttributeConstructor
    {
        enum TestEnum { STRING_ONE }
        public class ClassWithKeyedAttributeConstructor
        {
            private readonly string str;

            public ClassWithKeyedAttributeConstructor([KeyFilter(TestEnum.STRING_ONE)] string str) => this.str = str;

            public string GetStr() => str;
        }
        [TestMethod]
        public void TestClassWithKeyedAttributeConstructorCannotBeIntercepted()
        {
            var builder = new ContainerBuilder();
            builder.Register(c => "Hello").Keyed<string>(TestEnum.STRING_ONE);
            builder.Register(c => "World");
            //builder.RegisterType<ClassWithKeyedAttributeConstructor>().WithAttributeFiltering();
            builder.RegisterType<ClassWithKeyedAttributeConstructor>().WithAttributeFiltering().EnableClassInterceptors();
            var container = builder.Build();

            using var scope = container.BeginLifetimeScope();
            Assert.AreEqual("Hello", scope.Resolve<ClassWithKeyedAttributeConstructor>().GetStr());
        }
    }
}

Expected Behavior

Exception with Stack Trace

  Message: 
Assert.AreEqual failed. Expected:<Hello>. Actual:<World>. 

  Stack Trace: 
TestClassWithKeyedAttributeConstructor.TestClassWithKeyedAttributeConstructorCannotBeIntercepted() line 32

Dependency Versions

Autofac: 8.1.1
Autofac.Extras.DynamicProxy: 7.1.0
MSTest: 3.6.2

Additional Info

If we uncomment line 26 and comment line 27. The test will execute correctly.

Activity

  1. tillig commented on Nov 14, 2024

    @tillig
    Member

    I just wanted to jump in and say that your issue here has been seen but it may be a bit before someone can jump in and do anything about it. We're dreadfully short on PRs coming in and there are only a couple of active maintainers, both of whom are doing this in their shrinking spare time. If you want this solved faster, we'd very much welcome a PR that includes the fix and a test to validate the fix.

    My initial guess is that when the proxy is generated for the interceptor that we're not copying the attributes from the original class constructor parameters over to the interceptor constructor parameters. Is that right? Is there something else? I don't know without diving in and just solving the problem, which I don't have time for at this exact moment. But if you were going to try for a PR, that'd be a good place to start looking.

  2. elgatov commented on Nov 14, 2024

    @elgatov
    Author

    Hi tillig,

    I understand this is a side project and time is tight. I will try to check what you are pointing me too and see if I can fix it and get a PR, but as of right now I feel it is above my knowledge of c#.

    Thanks for the help

  3. elgatov commented on Nov 18, 2024

    @elgatov
    Author

    I believe the error is exactly in

    for each PositionalParameter it is trying to resolve the service without taking into account if it's keyed. Tell me if you believe this is indeed the problem to pursue it further.

    Thank you.

  4. tillig commented on Nov 18, 2024

    @tillig
    Member

    I think that's a reasonable place to start, for sure.

  5. tillig commented on Jun 24, 2026

    @tillig
    Member

    After digging into this, the root cause is a bug in Castle DynamicProxy, not in this library or in Autofac core.

    When EnableClassInterceptors generates the proxy subclass, Castle replicates the constructor parameter attributes onto the proxy's constructor. KeyFilterAttribute's constructor parameter is typed object (KeyFilterAttribute(object key)), so an enum key gets boxed — and Castle reproduces that boxed enum argument as its underlying integer rather than the original enum type.

    The attribute is still present on the proxy constructor, but KeyFilterAttribute.Key ends up as boxed (int)0 instead of TestEnum.STRING_ONE. Core's WithAttributeFiltering then looks up new KeyedService((int)0, typeof(string)), which doesn't match the registered KeyedService(TestEnum.STRING_ONE, typeof(string)) — so the filter silently misses (picking an unkeyed fallback if one exists, or failing to bind the constructor if not).

    Key points:

    • This only affects non-string keys (enums, etc.). String keys round-trip fine, because a string boxes into object losslessly.
    • It only affects EnableClassInterceptors. EnableInterfaceInterceptors doesn't rewrite the constructor, so the original attribute is read intact.
    • We're on the latest released Castle.Core (5.2.1); this is not fixed on their unreleased 6.0.0 branch either.

    I've filed an upstream issue with a minimal (Autofac-free) repro: castleproject/Core#748.

    Workarounds until Castle fixes it:

    • Use a string key with [KeyFilter], or
    • Use EnableInterfaceInterceptors instead of EnableClassInterceptors.

    We'll track the upstream issue and revisit once Castle ships a fix.

  6. changed the title [-]EnableClassInterceptors does not take into account KeyFilter-ed parameters in the constructor[/-] [+]EnableClassInterceptors does not take into account enum KeyFilter parameters in the constructor[/+] on Jun 24, 2026
  7. added a commit that references this issue on Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions