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

我想修改我当前的LetterFactory实现并通过调用容器来移除对Activator.CreateInstance的调用,以解析使用构造函数注入完全初始化的当前Letter。 我已经在这里和这里阅读了文档,甚至在撰写这篇文章时也发表了这篇文章,但似乎没有任何内容。

笔记:

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

UPDATE1

问题似乎从字母本身未注册的事实开始,如何从集合中收集收集字母的LINQ代码并注册那些enmass?

谢谢你,斯蒂芬

您正在寻找IIndex这是一种字典,它可以组成,因此IIndex>是您想要的类型。

有了这样的类型,您的LetterFactory将如下所示:

 public class LetterFactory : ILetterFactory { private readonly IIndex> _lettersFactory; public LetterFactory(IIndex> lettersFactory) { _lettersFactory = lettersFactory; } public LetterBase Create(int letterId) { if (letterId <= 0) { throw new ArgumentOutOfRangeException(nameof(letterId)); } Func 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 letterTypes = typeof(LetterBase).Assembly.GetTypes() .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(LetterBase))) .ToList(); foreach(Type letterType in letterTypes) { LetterTypeAttribute attribute = type.GetCustomAttributes() .First(); builder.RegisterType(letterType) .Keyed(attribute.LetterId); } 

您还将使用此代码提高性能:重型组件扫描仅在启动时发生一次,而不是每次调用。

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

您也可以直接依赖IIndex而不是IIndex>它取决于您的范围策略。

你让我做真正的工作,干得好:)以下是我的解决方案。

Autofac – 命名和键控服务 – 使用索引进行解析

 using System; using System.Collections.Generic; using System.Linq; using Autofac; using Autofac.Features.Indexed; public class Program { private static IContainer _Container; public static void Main() { InitDependencyInjection(); var rd1 = _Container.Resolve(new NamedParameter("letterId", 1)); rd1.PrintType(); var rd2 = _Container.Resolve(new NamedParameter("letterId", 2)); rd2.PrintType(); } private static void InitDependencyInjection() { var builder = new ContainerBuilder(); var letterTypes = typeof(LetterBase).Assembly.GetTypes() // Find all types that derice from LetterBase .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(LetterBase))) // Make sure they are decorated by attribute .Where(t => t.GetCustomAttributes(typeof(LetterTypeAttribute), false).Length == 1) .ToList(); //Register with Autofac, Keyed by LetterId //This should throw an exception if any are duplicated //You may want to consider using an enum instead //It's not hard to convert an Int to Enum foreach(Type letterType in letterTypes) { // we already tested the type has the attribute above var attribute = letterType .GetCustomAttributes(typeof(LetterTypeAttribute) , false)[0] as LetterTypeAttribute; builder.RegisterType(letterType) .Keyed(attribute.LetterId); } builder.RegisterType(); _Container = builder.Build(); } } public class RequiresDependency { private readonly LetterBase _letter; //Autofac automagically provides a factory that returns type //type you need via indexer public RequiresDependency(int letterId, IIndex letterFactory) { //resolve the needed type based on the index value passed in _letter = letterFactory[letterId]; } public void PrintType() { Console.WriteLine(_letter.GetType().Name); } } public abstract class LetterBase { } [LetterType(1)] public class LetterA : LetterBase {} [LetterType(2)] public class LetterB : LetterBase {} // make sure the classes using this attribute has only a single attribute [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class LetterTypeAttribute : Attribute { public LetterTypeAttribute(int letterId) { LetterId = letterId; } public int LetterId { get; private set; } } 

DotNetFiddle示例

结果:

LETTERA

LetterB