我想修改我当前的LetterFactory实现,并通过调用容器来解除对Activator.CreateInstance的调用,以解析使用构造函数注入完全初始化的当前Letter。我在阅读此帖后阅读了文档here和here,甚至this SO Post,但似乎没有任何内容。
注意:
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);
}
}
UPDATE1
这些问题似乎始于这样一个事实,即字母本身没有注册,如何使用从汇编中收集字母的LINQ代码并注册那些enmass?
谢谢你, 斯蒂芬
答案 0 :(得分:3)
您正在寻找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>>
,这取决于您的范围策略。
答案 1 :(得分:2)
你让我做真正的工作,做得好:)以下是我的解决方案。
Autofac - Named and Keyed Services - Resolving with Index
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<RequiresDependency>(new NamedParameter("letterId", 1));
rd1.PrintType();
var rd2 = _Container.Resolve<RequiresDependency>(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<LetterBase>(attribute.LetterId);
}
builder.RegisterType<RequiresDependency>();
_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<int, LetterBase> 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; }
}
结果:
LETTERA
LetterB