这是Configuring an Autofac delegate factory that's defined on an abstract class的后续问题。我已经实现了@Aren在他的回答中使用IIndex<K,V>
的建议,但我无法克服以下错误:
测试方法IssueDemoProject.WidgetTest.ProblemIllustration投掷 异常:Autofac.Core.DependencyResolutionException:无 在类型上使用“公共绑定标志”找到的构造函数 可以使用available调用'IssueDemoProject.WidgetWrangler' 服务和参数:无法解析参数 'IssueDemoProject.WidgetType widgetType'的构造函数'Void .ctor(Autofac.IComponentContext,IssueDemoProject.WidgetType)'。
更新:应该注意的是,如果我根据参数注册不同的具体类,那就有效。请参阅下面的第二个测试。
以下是一些说明问题的示例代码。 [编辑:我更新了相同的内容以使用IIndex查找。]
有人能告诉我我做错了吗?
using Autofac;
using Autofac.Features.Indexed;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace IssueDemoProject
{
public enum WidgetType
{
Sprocket,
Whizbang
}
public class SprocketWidget : Widget
{
}
public class WhizbangWidget : Widget
{
}
public abstract class Widget
{
}
public class WidgetWrangler : IWidgetWrangler
{
public Widget Widget { get; private set; }
public WidgetWrangler(IComponentContext context, WidgetType widgetType)
{
var lookup = context.Resolve<IIndex<WidgetType, Widget>>();
Widget = lookup[widgetType];
}
}
public interface IWidgetWrangler
{
Widget Widget { get; }
}
[TestClass]
public class WidgetTest
{
// NOTE: This test throws the exception cited above
[TestMethod]
public void ProblemIllustration()
{
var container = BuildContainer(
builder =>
{
builder.RegisterType<WidgetWrangler>().Keyed<IWidgetWrangler>(WidgetType.Sprocket).
InstancePerDependency();
builder.RegisterType<WidgetWrangler>().Keyed<IWidgetWrangler>(WidgetType.Whizbang).
InstancePerDependency();
}
);
var lookup = container.Resolve<IIndex<WidgetType, IWidgetWrangler>>();
var sprocketWrangler = lookup[WidgetType.Sprocket];
Assert.IsInstanceOfType(sprocketWrangler.Widget, typeof(SprocketWidget));
var whizbangWrangler = container.ResolveKeyed<IWidgetWrangler>(WidgetType.Whizbang);
Assert.IsInstanceOfType(whizbangWrangler.Widget, typeof(WhizbangWidget));
}
// Test passes
[TestMethod]
public void Works_with_concrete_implementations()
{
var container = BuildContainer(
builder =>
{
builder.RegisterType<SprocketWidget>().Keyed<Widget>(WidgetType.Sprocket).
InstancePerDependency();
builder.RegisterType<WhizbangWidget>().Keyed<Widget>(WidgetType.Whizbang).
InstancePerDependency();
});
var lookup = container.Resolve<IIndex<WidgetType, Widget>>();
var sprocketWrangler = lookup[WidgetType.Sprocket];
Assert.IsInstanceOfType(sprocketWrangler, typeof(SprocketWidget));
var whizbangWrangler = container.ResolveKeyed<Widget>(WidgetType.Whizbang);
Assert.IsInstanceOfType(whizbangWrangler, typeof(WhizbangWidget));
}
private IComponentContext BuildContainer(Action<ContainerBuilder> additionalRegistrations)
{
var assembly = GetType().Assembly;
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(assembly).AsSelf();
additionalRegistrations(builder);
IComponentContext container = builder.Build();
return container;
} }
}
答案 0 :(得分:3)
我认为你错了。主要是因为您的WidgetWrangler
期望工厂(IIndex<,>
有效地成为您的工厂)和WidgetType。
异常是因为您的WidgetType
枚举在Autofac中没有默认注册,而且可能不应该。 Autofac无法确定要传递给构造函数的值,因为我猜你正在尝试使用Autofac来解析你的WidgetWrangler
。
为了从Autofac中解析某些内容,它必须至少包含一个可以通过Autofac注册完全解析的构造函数,或者显式地将变量传递给解析操作(请注意,此方法具有反射性且非常慢)。
我假设整个事情的目标是有一些方法来检索Widget
某个WidgetType
的新子类。如果是这种情况,则所有您需要的是IIndex<,>
。
无论您何时需要创建新的小部件,只需使用IIndex&lt;,&gt;类型。这很简单。
如果您需要在实例化的同时执行另一个操作,则应将IIndex包装在使用IIndex的工厂类中,然后执行操作。
例如:
class Widget
{
// Stuff...
public virtual void Configure(XmlDocument xmlConfig)
{
// Config Stuff
}
}
interface IWidgetFactory
{
Widget Create(WidgetType type, XmlDocument config);
}
class WidgetFactory : IWidgetFactory
{
private readonly IIndex<WidgetType, Widget> _internalFactory;
public WidgetFactory(IIndex<WidgetType, Widget> internalFactory)
{
if (internalFactory == null) throw new ArgumentNullException("internalFactory");
_internalFactory = internalFactory;
}
public Widget Create(WidgetType type, XmlDocument config)
{
Widget instance = null;
if (!_internalFactory.TryGetValue(type, out instance))
{
throw new Exception("Unknown Widget Type: " + type.ToString);
}
instance.Configure(config);
return instance;
}
}
然后您可以简单地使用包装工厂:
class SomethingThatNeedsWidgets
{
private readonly IWidgetFactory _factory;
public SomethingThatNeedsWidgets(IWidgetFactory factory)
{
if (factory == null) return new ArgumentNullException("factory");
_factory = factory;
}
public void DoSomething()
{
Widget myInstance = _factory.Create(WidgetType.Whizbang, XmlDocument.Load("config.xml"));
// etc...
}
}
请记住,如果除了获取Widget
实例之外不需要执行任何操作,则IIndex<WidgetType, Widget>
本身就是一个工厂。 (假设所有已注册的Widget
子类都已注册InstancePerDependency
。否则它是一个实例选择器。