2

I think I get most things about dependency inversion and using an IoC container, but one thing still does not appear clear to me. How do I use autofac to automate the following factory:

public class WidgetFactory
{
    public static IWidget Create(int foo, double bar)
    {
        return new Widget(foo, bar);
    }
}

public class Widget
{
    private readonly int foo;
    private readonly double bar;

    public Widget(int foo, double bar)
    {
        this.foo = foo;
        this.bar = bar;
    }
}

elsewhere...

public class FoobarUser
{
    public void Method()
    {
        var widget = WidgetFactory.Create(3, 4.863);
        // Do something with my widget
        // Possibly add it to a widget collection
    }
}

Basically, I need thousands of widgets to be created and I'm not sure of the best way of doing so. How would I create the widget factory using autofac and how would I use that in Method, bearing in mind that Method does not contain a reference to the IContainer?

Stephen
  • 471
  • 6
  • 16

3 Answers3

7

The way to fix this problem is the following:

Change WidgetFactory to define a delegate for creating widgets:

public class WidgetFactory
{
    public delegate IWidget Create(int firstParam, double secondParam);
}

In your autofac module, wire up the factory using the RegisterGeneratedFactory method. This will automatically create your factory for you:

public class TestClassModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        base.Load(builder);

        builder.RegisterType<Widget>().As<IWidget>();
        builder.RegisterGeneratedFactory<WidgetFactory.Create>(new TypedService(typeof(IWidget)));

        builder.RegisterType<FoobarUser>();
    }
}

Inject the factory into FoobarUser:

public class FoobarUser
{

    private readonly WidgetFactory.Create factory;

    public FoobarUser(WidgetFactory.Create factory)
    {
        this.factory = factory;
    }

    public void Method()
    {
        var widget = this.factory(3, 4.836);
        // Do something with my widget
        // Possibly add it to a widget collection
    }
}
Stephen
  • 471
  • 6
  • 16
2

There are basically two ways to handle parameters:

  • At registration time - you can provide them in lambda registrations (Register(c => T)) or you can append parameters to reflection-based (RegisterType<T>) registrations.
  • At resolve time - you can either append parameters to Resolve<T>() calls or you can use delegate factories or Func<T> dependencies to dynamically create a factory method that can be used by your component.

There is robust documentation on all of these options with examples over at the Autofac documentation site:

Travis Illig
  • 23,195
  • 2
  • 62
  • 85
1

You would inject dependencies into your factory with an IoC container using constructor or property injection, not args into a method. If you needed to inject specific values as parameters into your service's constructor, you could set that up during registration similar to the below code.

Here, I'm getting a XML file path from my web.config and passing that value into my repository's constructor:

var builder = new ContainerBuilder();
var xmlFileName = HttpContext.Current.Server.MapPath(
    ConfigurationManager.AppSettings["xmlData"]);

builder.Register(c => new XmlAdvertisementRepository(new XmlContext(xmlFileName)))
    .AsImplementedInterfaces()
    .InstancePerHttpRequest();
agartee
  • 445
  • 2
  • 15
  • The problem is that you don't know at compile time what the values will be - they're dynamically generated at runtime and the application can use thousands of unique instances at a time. – Stephen Jul 22 '14 at 03:10
  • If they're not dependencies (services in most cases), then you would not inject them. A factory method like you've created seems appropriate. – agartee Jul 22 '14 at 03:20