structuremapopen-generics

Register open generic types for all types implementing some interface with StructureMap


I want to register all my types implementing IManager so that they can be used as the type T for the generic Lazy<T> class.

For example:

public TetraRadioPropertyUpdater(Lazy<IRadioManager> lazyRadioManager)

I use a self made scanner because my concrete types and interfaces are internal and therefor I cannot use the built in StructureMap scan mechanism.

In the first statement of the loop register all my IManager types like For<IRadioManager>().Singleton().Use<RadioManager>()

As well, I want them to be registered so that they can be used as the generic type for Lazy<T> like For<Lazy<IRadioManager>().Use<Lazy<RadioManger>>()

 InterfaceScanner<IManager> interfaceScanner = new InterfaceScanner<IManager>();

 // managerMapping looks like:
 // { IRadioManager, RadioManager }
 // { ICallManager, CallManager }
 // .. more manager interface to plugin type pairs  
 foreach (KeyValuePair<Type, Type> managerMapping in interfaceScanner.Process())
 {
    // the key is the plugin type, value is the concrete type
    For(managerMapping.Key).Singleton().Use(managerMapping.Value);

    // something like this.. ?
    For(typeof(Lazy<>)).Singleton().Use(c => new Lazy(() => c.GetInstance(managerMapping.Value)));
 }

Is this possible? How do I need to configure it for StructureMap?


Solution

  • UPDATE: StructureMap v3 implements this out of the box, so this trick is no longer necessary.


    You can explicitly register the classes like this:

    container = new Container(x =>
    {
        x.Scan(y =>
        {
            y.TheCallingAssembly();
            y.WithDefaultConventions();
        });
    
        x.For<Lazy<IFoo>>().Use(y => new Lazy<IFoo>(y.GetInstance<Foo>));
        x.For<Lazy<IBar>>().Use(y => new Lazy<IBar>(y.GetInstance<Bar>));
        x.For<Lazy<IBaz>>().Use(y => new Lazy<IBaz>(y.GetInstance<Baz>));
    });
    

    It would be nicer if this were done automatically, though. Ideally, the following syntax would be nice.

    x.For(typeof(Lazy<>)).Use(typeof(Lazy<>));
    

    Unfortunately, at runtime, StructureMap will attempt to find the "greediest" constructor for Lazy<T> and settle on public Lazy(Func<T> valueFactory, bool isThreadSafe). Since it doesn't know what to do with the boolean isThreadSafe parameter. It will throw an exception. You can explicitly tell StructureMap what value to use like this.

    x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
     .CtorDependency<bool>("isThreadSafe").Is(true);
    

    Which will stop the exceptions, and use the value of "true" for the isThreadSafe parameter. The documentation for Lazy states that the thread safety mode of the default Lazy(Func<T> valueFactory) constructor is LazyThreadSafetyMode.ExecutionAndPublication, which is also what you get by passing true into the isThreadSafe parameter of the constructor above, so we'll get the same behavior as if we called the constructor we actually wanted to use in the first place (e.g. Lazy(Func<T> valueFactory)). You can read about the Lazy class in more detail here.

    I realize that this doesn't directly answer your question with regard to the custom scanner class, but it should give you a good start, I would hope.