c# - 如何将 Autofac 容器用作抽象工厂?

标签 c# autofac

我想修改我当前的 LetterFactory 实现,并通过调用容器来删除对 Activator.CreateInstance 的调用,以解析使用构造函数注入(inject)完全初始化的当前 Letter。我已阅读文档 herehere ,甚至 this所以在写这篇文章时发布,但似乎没有点击。

注意事项:

1) IDocumentServicesCore 是一个聚合。

2) 所有字母都用 LetterTypeAttribute 装饰(有数百个)

3)这个LetterFactory本身是在容器中注册的。

public class LetterFactory : ILetterFactory
{
    private readonly IDocumentServicesCore _documentServicesCore;

    public LetterFactory(IDocumentServicesCore documentServicesCore)
    {
        _documentServicesCore = documentServicesCore;
    }

    public LetterBase Create(int letterId)
    {
        if (letterId <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(letterId));
        }

        List<Type> types = typeof(LetterBase).Assembly.GetTypes()
            .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(LetterBase)))
            .ToList();

        LetterBase letter = null;
        foreach(Type type in types)
        {
            LetterTypeAttribute attribute = type.GetCustomAttributes<LetterTypeAttribute>().First();

            if (!attribute.LetterId.Contains(letterId))
            {
                continue;
            }

            letter = Activator.CreateInstance(type, _documentServicesCore) as LetterBase;
            break;
        }

        if (letter != null)
        {
            return letter;
        }

        string message = $"Could not find a LetterBase to create for id {letterId}.";
        throw new NotSupportedException(message);
    }
}

更新1

问题似乎始于字母本身未注册这一事实,我如何获取从程序集中收集字母的 LINQ 代码并注册这些字母?

谢谢, 斯蒂芬

最佳答案

您正在寻找IIndex<TKey, TValue>这是一种字典,它可以这样组成IIndex<Int32, Func<LetterBase>>是你想要的类型。

有了这样的类型你的LetterFactory看起来像这样:

public class LetterFactory : ILetterFactory
{
    private readonly IIndex<Int32, Func<LetterBase>> _lettersFactory; 
    public LetterFactory(IIndex<Int32, Func<LetterBase>> lettersFactory)
    {
        _lettersFactory = lettersFactory;
    }

    public LetterBase Create(int letterId)
    {
        if (letterId <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(letterId));
        }

        Func<LetterBase> letterFactory = null; 
        if(!this._lettersFactory.tryGetValue(letterId, out letterFactory))
        {
            string message = $"Could not find a LetterBase to create for id {letterId}.";
            throw new NotSupportedException(message);        
        }

        Letter letter = letterFactory(); 
        return letter; 
    }
}

然后你必须像这样注册你的类型:

List<Type> letterTypes = typeof(LetterBase).Assembly.GetTypes()
    .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(LetterBase)))
    .ToList();

foreach(Type letterType in letterTypes)
{
    LetterTypeAttribute attribute = type.GetCustomAttributes<LetterTypeAttribute>()
                                        .First();

    builder.RegisterType(letterType)
           .Keyed<LetterBase>(attribute.LetterId);
}

您还将使用此代码提高性能:繁重的程序集扫描只会在启动时发生一次,而不是每次调用都会发生。

顺便说一句,请注意 IIS 托管应用程序中的程序集扫描限制:http://autofaccn.readthedocs.io/en/latest/register/scanning.html#iis-hosted-web-applications

也可以直接依赖IIndex<Int32, LetterBase>而不是 IIndex<Int32, Func<LetterBase>>这取决于您的范围策略。

关于c# - 如何将 Autofac 容器用作抽象工厂?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50329867/

相关文章:

c# - 从 channel 返回YouTube API获取视频,仅返回设备支持视频网址

asp.net - 为后台线程配置 Autofac 容器

c# - 如何在 .NET 5 控制台应用程序中使用 Autofac 和选项模式?

asp.net-mvc - 在 autofac 中使用命名注册与 MVC Controller 注入(inject)集成

asp.net-mvc - 使用 NHibernate 和 Autofac 管理多个数据库

c# - 使用 Visual C# 将图像添加到 SQL 数据库

c# - 在 setter 中确定调用者——或静默设置属性

c# - 用 C# 编写一个简单的 Web 服务并从 Ruby on Rails 调用它

C# try catch 混淆

c# - hangfire 作业激活和 Autofac 的问题