0
votes

I'm struggling to make StructureMap use one of concrete types sharing a common interface. This is further complicated by the fact that all candidate objects are descendants of an intermediate abstract class.

public interface ICustomer
{
    string Id { get; }
}

public abstract class CommonCustomer : ICustomer {
    public abstract string Id { get; }
}

// Fallback type if none matched
public class BaseCustomer : CommonCustomer
{
    public override string Id { get; } = "Base";
}

// Concrete type 1
public class AlphaCustomer : CommonCustomer
{
    public override string Id { get; } = "Alpha";
}

// Concrete type 2
public class BravoCustomer : CommonCustomer
{
    public override string Id { get; } = "Bravo";
}

What I tried so far:

Scan(x =>
{
    x.TheCallingAssembly();
    x.AddAllTypesOf<ICustomer>();
});

var key = "Alpha";

For<ICustomer>().Use("",
    context => context.GetAllInstances<ICustomer>()
        .FirstOrDefault(x => x.Id == key)).Singleton();
For<ICustomer>().UseIfNone<BaseCustomer>().Singleton();

How can I select a concrete type based on it's string property? And how do I scan through types which do not directly implement ICustomer?

1
Why exactly are the types not implementing ICustomer? There's no direct or indirect connection between your concrete types and that interface this way. - Alen Genzić
Sorry, see my edit. CommonCustomer implements ICustomer. - ttaaoossuuuu
How are you using this? What is the scenario where key changes? It looks like you need a factory to instantiate your ICustomer. - Torbjörn Hansson

1 Answers

0
votes

Sounds like you want to create a factory for instantiating ICustomer.

public interface ICustomerFactory
{
    ICustomer Create(string key);
}

public class CustomerFactory : ICustomerFactory
{
    private readonly IContainer _container;
    public CustomerFactory(IContainer container)
    {
        _container = container;
    }
    public ICustomer Create(string key) => _container.TryGetInstance<ICustomer>(key);
}

And during configuration of your container naming them:

var container = new Container(c =>
{
    c.For<ICustomerFactory>().Use<CustomerFactory>();
    c.Scan(x =>
    {
        x.TheCallingAssembly();
        x.AddAllTypesOf(typeof(ICustomer))
            .NameBy(t => ((ICustomer)Activator.CreateInstance(t, new object[0], new object[0])).Id);
    });
});

Usage:

ICustomerFactory factory;
var customer1 = factory.Create("Alpha");
var customer2 = factory.Create("Bravo");
var customer3 = factory.Create("Base");
var customer4 = factory.Create("NotExisting"); // returns null.