Andrey Shchekin
Andrey Shchekin

Reputation: 21599

Autofac: Overriding registration target type from a module

I want to create an Autofac Module that overrides registration target type based on certain criteria. However the new type would have the same constructor and should be created using same parameter overrides as the original type.

I can use AttachToComponentRegistration to decide whether the registration should be overridden, but the overriding itself poses a problem. I suppose I need to replace the IInstanceActivator (specifically ReflectionActivator), but I do not see a way to get full information about existing ReflectionActivator — there seems to be no property to get configured parameters, for example.

Example (simplified code):

protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
    base.AttachToComponentRegistration(componentRegistry, registration);
    var reflectionActivator = ((ComponentRegistration)registration).Activator as ReflectionActivator;
    if (reflectionActivator == null)
        return;

    var replacementType = ReplaceType(reflectionActivator.LimitType);
    if (replacementType == reflectionActivator.LimitType)
        return;

    ((ComponentRegistration)registration).Activator = new ReflectionActivator(
         replacementType,
         reflectionActivator.ConstructorFinder,
         reflectionActivator.ConstructorSelector,
         configuredParameters: ???, // how to get this?
         configuredProperties: ???  // or this?
    );
}

Is this something that can be done easier and am I just missing something here?

Upvotes: 3

Views: 2235

Answers (1)

Alexandr Nikitin
Alexandr Nikitin

Reputation: 7436

ReflectionActivator contains configuredParameters and configuredProperties in its private fields. You can read them via Reflection

var configuredParameters = (IEnumerable<Parameter>)typeof(ReflectionActivator).GetField("_configuredParameters", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(reflectionActivator);

var configuredProperties = (IEnumerable<Parameter>)typeof(ReflectionActivator).GetField("_configuredProperties", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(reflectionActivator);

And a working example:

class Program
{
    interface IInterface
    {
    }

    class MyClass : IInterface
    {
        private readonly string _name;

        public MyClass(string name)
        {
            _name = name;
        }

        protected MyClass()
        {
        }

        public override string ToString()
        {
            return string.Format("{0} {1}", GetType(), _name);
        }
    }

    class MyReplacementClass : MyClass
    {
        private readonly string _name;

        public MyReplacementClass(string name)
        {
            _name = name;
        }

        public override string ToString()
        {
            return string.Format("{0} {1}", GetType(), _name);
        }
    }

    class MyModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<MyClass>().As<IInterface>().WithParameter("name", "Parameter");
        }

        protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
            IComponentRegistration registration)
        {
            var reflectionActivator = ((ComponentRegistration) registration).Activator as ReflectionActivator;
            if (reflectionActivator == null)
                return;

            var replacementType = ReplaceType(reflectionActivator.LimitType);
            if (replacementType == reflectionActivator.LimitType)
                return;

            var configuredParameters = (IEnumerable<Parameter>)typeof(ReflectionActivator).GetField("_configuredParameters",
                BindingFlags.NonPublic | BindingFlags.Instance).GetValue(reflectionActivator);

            var configuredProperties = (IEnumerable<Parameter>)typeof(ReflectionActivator).GetField("_configuredProperties",
                BindingFlags.NonPublic | BindingFlags.Instance).GetValue(reflectionActivator);

            ((ComponentRegistration) registration).Activator = new ReflectionActivator(
                replacementType,
                reflectionActivator.ConstructorFinder,
                reflectionActivator.ConstructorSelector,
                configuredParameters,
                configuredProperties
                );
        }

        private Type ReplaceType(Type limitType)
        {
            return typeof (MyReplacementClass);
        }
    }

    static void Main(string[] args)
    {
        var builer = new ContainerBuilder();
        builer.RegisterModule<MyModule>();

        using (var container = builer.Build())
        {
            var myClass = container.Resolve<IInterface>();
            Console.WriteLine(myClass);
            Console.ReadKey();
        }
    }
}

Upvotes: 1

Related Questions