2011-09-26 150 views
2

这是针对Configuring an Autofac delegate factory that's defined on an abstract class的后续问题。我实现了使用IIndex<K,V>这@Aren在他的回答让的建议,但我无法克服以下错误:Autofac通过IIndex解决有关键控注册的问题

Test method IssueDemoProject.WidgetTest.ProblemIllustration threw exception: Autofac.Core.DependencyResolutionException: None of the constructors found with 'Public binding flags' on type 'IssueDemoProject.WidgetWrangler' can be invoked with the available services and parameters: Cannot resolve parameter 'IssueDemoProject.WidgetType widgetType' of constructor '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; 
    } } 
} 

回答

3

我想你是在讨论这个错误。主要是因为您的WidgetWrangler期望工厂(IIndex<,>有效成为您的工厂)和WidgetType。

例外是因为您的WidgetType枚举在Autofac中没有默认注册,它可能不应该。 Autofac无法弄清楚什么值传入构造函数,因为我猜你正在尝试使用Autofac来解决你的WidgetWrangler

为了解决Autofac的一些问题,它必须至少包含一个可以完全由Autofac的注册解决的构造函数,或者你明确地将变量传递给解析操作(请注意,这种方法是反射式的,速度非常慢) 。

我假设这整个事情的目标是有一些方法来检索Widget新的子类给定WidgetType某处。如果是这种情况,那么你需要的是IIndex<,>

无论您需要创建新的小工具,只需使用IIndex <>>类型即可。这很简单。

如果您需要执行另一个操作以及您的实例化,您应该将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注册的,否则它是一个实例选择器

+0

你说得对,我在想这个错误,谢谢你抽出时间打出详细的答案,我终于能够看到我在哪里出错了,并且工作正常,非常感谢! – neontapir