配置 autofac 以忽略标记为过时的构造函数

发布于 2024-11-16 08:28:26 字数 595 浏览 3 评论 0原文

是否可以轻松配置 autofac,使其仅使用非过时的构造函数进行解析?

例如,对于具有非 DI 代码的辅助构造函数的类,

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>();

如果我们尚未注册 MyService,则要求 autofac 解析 Example 应该会失败。

Is it possible to easily configure autofac so it will only resolve using non-obsolete 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

厌味 2024-11-23 08:28:26

我不相信有一种开箱即用的方法来配置 Autofac 来忽略过时的构造函数。但是,Autofac 非常好,总有办法完成它:) 这里有两个选项:

选项 1. 告诉 Autofac 使用哪个构造函数

使用 UsingConstructor 注册扩展方法。

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

选项 2. 向 FindConstructorsWith 提供自定义 IConstructorFinder

Autofac 有一个名为 FindConstructorsWith 的注册扩展方法。您可以将自定义 IConstructorFinder 传递给两个重载之一。您可以编写一个名为 NonObsoleteConstructorFinder 的简单 IConstructorFinder,它只会返回不带 Obsolete 属性的构造函数。

我已经编写了这个课程并添加了您的示例的工作版本。您可以查看完整代码并将其用作灵感。 IMO 选项,这是更优雅的选项。我已将其添加到 GitHub 上的 AutofacAnswers 项目中。

注意:另一个重载采用 BindingFlags。我认为您无法使用 BindingFlags 指定属性要求。不过,您可能想检查一下。

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.

云柯 2024-11-23 08:28:26

这是 Bentayloruk 答案的延伸。我尝试了他的选项 2,但没有成功。很快我注意到这是因为我使用了 AutoFac 拦截器。 AutoFac 将代理类类型传递给构造函数查找器,但这些构造函数没有在基础类的构造函数上定义的属性。

为了完成这项工作,我的代码会比较两个类的构造函数签名,以找到“正确的”构造函数并检查该属性是否存在。

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();
    }
}

注释 1: Skip(1) 是必需的,因为代理的第一个构造函数参数的类型为 IInterceptor[]。 AutoFac 使用它来传递拦截器。

注2: targetType.Implements()underlyingConstructor.HasAttribute() 是 Fasterflect 库提供的扩展方法。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文