Fork me on GitHub

Auto-factories Edit on GitHub


When you need to implement Abstract Factory, StructureMap offers a way to do it for you. Let's say you have


public interface IDummyService
{
    string Name { get; }
}

with implementation

public class DummyService : IDummyService
{
    public string Name { get; set; }
}


Now you declare an interface for your factory:


public interface ISimpleDummyFactory
{
    IDummyService CreateDummyService();
}


All you need to do is to call CreateFactory when configuring the container as shown below:


[Fact]
public void Simple_factory_creation()
{
    var container = new Container(cfg =>
    {
        cfg.For<IDummyService>().Use<DummyService>();
        cfg.For<ISimpleDummyFactory>().CreateFactory();
    });

    var factory = container.GetInstance<ISimpleDummyFactory>();

    var component = factory.CreateDummyService();

    component.ShouldNotBeNull();
    component.ShouldBeOfType<DummyService>();
}


Default convention

As for now, Auto-factories support two types of methods:

  1. Methods to list all names of registered implementations.
  2. Methods to create object instances i.e. factory methods.

To declare a method that has to return the names of registered implementations, the method signature must satisfy the following conditions:

  1. The name has to start with GetNames
  2. It must be a generic method.
  3. The method return type must be assignable from List<string> e.g. IList<string>, IEnumerable<string>

Any other method that has the return type (i.e. doesn't return void), is treated as a factory method. In addition, if the method name starts with GetNamed, the first method argument is used as the name for the named instance. All the rest arguments are passed as explicit arguments to the implementation constructor.

It is much easier to see it on an example:


public interface IDummyFactory
{
    // This method will return the names of all registered implementations of TService.
    IList<string> GetNames<TService>();

    // This method will just create the default IDummyService implementation.
    IDummyService CreateDummyService();

    // This method will just create the default IDummyService implementation and pass namePart1 and namePart2 as
    // dependencies.
    IDummyService CreateDummyService(string namePart1, string namePart2);

    // This method will create IDummyService implementation with serviceName name.
    IDummyService GetNamedDummyService(string serviceName, string namePart1, string namePart2);

    // Generic methods are also allowed as factory methods.
    TService CreateService<TService>();

    // Something that is common for event-sourcing implementations.
    IHandler<TMessage> CreateHandler<TMessage>();
}


Custom convention

If the default convention doesn't work for you, you can create and use your custom convention. All you need is to implement IAutoFactoryConventionProvider and use the corresponding CreateFactory overload. IAutoFactoryConventionProvider has a single method to implement:


IAutoFactoryMethodDefinition GetMethodDefinition(MethodInfo methodInfo, IList<object> arguments);


IAutoFactoryMethodDefinition is defined as follows:


/// <summary>
/// Describes how AutoFactory should treat the specific method declared in an abstract factory interface.
/// </summary>
public interface IAutoFactoryMethodDefinition
{
    /// <summary>
    /// The method type. See <see cref="AutoFactoryMethodType"/> for possible values.
    /// </summary>
    AutoFactoryMethodType MethodType { get; }

    /// <summary>
    /// The instance type to create.
    /// </summary>
    Type InstanceType { get; }

    /// <summary>
    /// The instance name if available.
    /// </summary>
    string InstanceName { get; }

    /// <summary>
    /// Explicit arguments if available.
    /// </summary>
    ExplicitArguments ExplicitArguments { get; }
}