开发者

Configure autofac to ignore constructors marked as obsolete

开发者 https://www.devze.com 2023-03-14 13:01 出处:网络
Is it possible to easily configure autofac so it will only resolve using non-obs开发者_运维问答olete constructors?

Is it possible to easily configure autofac so it will only resolve using non-obs开发者_运维问答olete constructors?

eg for a class with a helper constructor for non-DI code,

public class Example {

    public Example(MyService service) {
        // ...
    }

    [Obsolete]
    public Example() {
        service = GetFromServiceLocator<MyService>();
        // ...
    }
}

// ....

var builder = new ContainerBuilder();
builder.RegisterType<Example>();
// no MyService defined.
var container = builder.Build();

// this should throw an exception
var example = container.Resolve<Example>();

asking autofac to resolve Example if we haven't registered MyService, should fail.


I don't believe there is an out of the box way to configure Autofac to ignore Obsolete constructors. However, Autofac is so good, there is always a way to get it done :) Here are two options:

Option 1. Tell Autofac which Constructor to use

Do this using the UsingConstructor registration extension method.

builder.RegisterType<Example>().UsingConstructor(typeof(MyService));

Option 2. Provide a custom IConstructorFinder to FindConstructorsWith

Autofac has a registration extension method called FindConstructorsWith. You can pass a custom IConstructorFinder to one of the two overloads. You could write a simple IConstructorFinder called NonObsoleteConstructorFinder that will only return constructors without the Obsolete attribute.

I have written this class and added a working version of your sample. You can view the full code and use it as inspiration. IMO option this is the more elegant option. I have added it to my AutofacAnswers project on GitHub.

Note: The other overload takes BindingFlags. I don't think you can specify attribute requirements using BindingFlags. However, you may want to check that.


This is an extension to bentayloruk's answer. I tried his Option 2 but it didn't work. Soon I noticed that this is because I'm using AutoFac Interceptors. AutoFac passes the proxy class type to the constructor finder but these constructors don't have the attributes defined on the constructors in the underlying class.

To make this work my code compares the constructor signatures of both classes to find the "correct" constructor and checks if the attribute is present.

public class NonObsoleteConstructorFinder : IConstructorFinder
{
    private readonly DefaultConstructorFinder _defaultConstructorFinder = new DefaultConstructorFinder();

    public ConstructorInfo[] FindConstructors(Type targetType)
    {
        // Find all constructors using the default finder
        IEnumerable<ConstructorInfo> constructors = _defaultConstructorFinder.FindConstructors(targetType);

        // If this is a proxy, use the base type
        if (targetType.Implements<IProxyTargetAccessor>())
        {
            // It's a proxy. Check for attributes in base class.
            Type underlyingType = targetType.BaseType;
            List<ConstructorInfo> constructorList = new List<ConstructorInfo>();

            // Find matching base class constructors
            foreach (ConstructorInfo proxyConstructor in constructors)
            {
                Type[] parameterTypes = proxyConstructor.GetParameters()
                                                        .Select(pi => pi.ParameterType)
                                                        .Skip(1)    // Ignore first parameter
                                                        .ToArray();

                ConstructorInfo underlyingConstructor = underlyingType.GetConstructor(parameterTypes);

                if (underlyingConstructor != null &&
                    !underlyingConstructor.HasAttribute<ObsoleteAttribute>())
                {
                    constructorList.Add(proxyConstructor);
                }
            }

            constructors = constructorList;
        }
        else
        {
            // It's not a proxy. Check for the attribute directly.
            constructors = constructors.Where(c => !c.HasAttribute<ObsoleteAttribute>());
        }

        return constructors.ToArray();
    }
}

Note 1: Skip(1) is required because the first constructor argument of the proxy is of type IInterceptor[]. This is used by AutoFac to pass the interceptors.

Note 2: targetType.Implements<IProxyTargetAccessor>() and underlyingConstructor.HasAttribute<ObsoleteAttribute>() are extension methods provided by the Fasterflect library.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号