This is a follow-up question to Configuring an Autofac delegate factory that's defined on an abstract class. I’ve implemented the suggestion of using IIndex<K,V> that @Aren made in his answer, but I’m unable to overcome the following error:
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)’.
UPDATE: It should be noted that if I register different concrete classes based on a parameter, that works. See the second test below.
Here is some sample code that illustrates the issue. [EDIT: I updated the same to use an IIndex lookup.]
Can someone tell me what I’m doing wrong?
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;
} }
}
I think you are going about this wrong. Mainly because your
WidgetWrangleris expecting both the factory (IIndex<,>effectively becomes your factory), and a WidgetType.The exception is because your
WidgetTypeenum doesn’t have a default registration in Autofac, and it probably shouldn’t. Autofac cannot figure out what value to pass into the constructor as I’m guessing you’re trying to use Autofac to Reolve yourWidgetWrangler.In order to resolve something from Autofac, it must contain at least one constructor that can be resolved entirely by Autofac’s Registrations, OR with you explicitly passing variables to the resolve operation (please note, this method is reflective and extremely slow).
I am assuming the goal of this whole thing is to have some way to retrieve a new Subclass of
Widgetgiven aWidgetTypesomewhere. If this is the case, then ALL YOU NEED is theIIndex<,>.Wherever you need to create new Widgets you just use the IIndex<,> type. It’s simple.
If you need to perform another operation along with your instantiation you should wrap the IIndex in a factory class that uses IIndex then performs the actions.
For Example:
You can then just used the wrapped factory simply:
Remember, If you don’t need to do anything other than get the
Widgetinstance back, yourIIndex<WidgetType, Widget>is a factory in itself. (assuming all registeredWidgetsubclasses areInstancePerDependencyregistered. Otherwise it’s an instance selector.