4

I'm working on a C# project. I'm trying to get rid of a Factory class that has a large switch statement.

I want to configure Autofac to be able to construct a dependency based on a parameter, thereby allowing Autofac to take the place of the Factory.

I've looked at the DelegateFactories page of the Autofac wiki, but I can't figure out how to apply the pattern to an abstract class. Here's some code showing the situation:

public enum WidgetType
{
    Sprocket,
    Whizbang
}

public class SprocketWidget : Widget
{
}

public class WhizbangWidget : Widget
{
}

public abstract class Widget
{
    public delegate Widget Factory(WidgetType widgetType);
}

public class WidgetWrangler
{
    public Widget Widget { get; private set; }

    public WidgetWrangler(IComponentContext context, WidgetType widgetType)
    {
        var widgetFactory = context.Resolve<Widget.Factory>();
        Widget = widgetFactory(widgetType);
    }
}

I'd like it if I were to say new WidgetWrangler(context, WidgetType.Sprocket), its Widget property would be a SpocketWidget.

When I try this, I get errors stating that Widget.Factory is not registered. Does this delegate factory pattern not work with abstract classes, and if so, is there another way to accomplish this?

neontapir
  • 4,698
  • 3
  • 37
  • 52

1 Answers1

13

What you're looking for is the IIndex<,> Relationship Type.

If you register your sub-classes with .Keyed<>(...) you can key a registration to a value (object).

For example:

builder.RegisterType<SprocketWidget>()
   .Keyed<Widget>(WidgetType.Sproket)
   .InstancePerDependency();

builder.RegisterType<WhizbangWidget>()
   .Keyed<Widget>(WidgetType.Whizbang)
   .InstancePerDependency();

Then you only require a dependency of IIndex<WidgetType,Widget> to mimic factory behaviour.

public class SomethingThatUsesWidgets
{    
    private readonly IIndex<WidgetType,Widget> _widgetFactory;
    public SomethingThatUsesWidgets(IIndex<WidgetType,Widget> widgetFactory)
    {
        if (widgetFactory == null) throw ArgumentNullException("widgetFactory");
        _widgetFactory = widgetFactory;
    }

    public void DoSomething()
    {
        // Simple usage:
        Widget widget = widgetFactory[WidgetType.Whizbang];

        // Safe Usage:
        Widget widget2 = null;
        if(widgetFactory.TryGetValue(WidgetType.Sprocket, out widget2))
        {
            // do stuff
        }
    }
}

That's using Dependency Injection approach, if you just want to resolve the factory:

var factory = Container.Resolve<IIndex<WidgetType,Widget>>();
Aren
  • 54,668
  • 9
  • 68
  • 101
  • +1 `IIndex` is definitely the approach, given that the OP specified an enumeration. I would update your example, though, to store the factory in a member variable and use it in a method call. That keeps the initialization phase separate from the runtime phase, substantially reducing timing issues. See the Modem example here: http://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices – Bryan Watts Sep 23 '11 at 03:54
  • I've updated the example to something more along the lines of how one would use this. The original example was just a quickie to see the implementation code. – Aren Sep 24 '11 at 00:10