this is probably just a newbie question, but I have the following:
public class FooSettings {}
public class BarSettings {}
public class DohSettings {}
// There might be many more settings types...
public interface IProcessor { ... }
public class FooProcessor
: IProcessor
{
public FooProcessor(FooSettings) { ... }
}
public class BarProcessor
: IProcessor
{
public BarProcessor(BarSettings) { ... }
}
public class DohProcessor
: IProcessor
{
public DohProcessor(DohSettings) { ... }
}
// There might be many more processor types with matching settings...
public interface IProcessorConsumer {}
public class ProcessorConsumer
: IProcessorConsumer
{
public ProcessorConsumer(IProcessor processor) { ... }
}
An instance of either FooSettings or BarSettings is provided from an external source i.e.:
object settings = GetSettings();
And now I would like to resolve ProcessorConsumer based on injecting the existing instance of settings e.g.:
container.RegisterAssemblyTypes(...); // Or similar
container.Inject(settings);
var consumer = container.Resolve<IProcessorConsumer>();
That is if an instance of FooSettings is provided then a FooProcessor is created and injected into the ProcessorConsumer which is then the instance resolved.
I haven't been able to figure out how to do this in either StructureMap, Ninject nor Autofac... probably because I am a newbie when it comes to IoC containers. So answers for all of these or other containers so they can be compared would be highly appreciated.
UPDATE: I am looking for a solution which easily allows for new settings and processors to be added. Also there will be a one-to-on mapping from settings type to processor type. But which also allows for other instances/services to be injected in a given processor type, based on its constructor parameters. I.e. some processor might need a IResourceProvider service or similar. Just an example here.
Ideally, I would like something like
container.For<IProcessor>.InjectConstructorParameter(settings)
or similar. Thereby, guiding the IoC container to use the processor type matching the injected constructor parameter instance.
You don't want dependency injection for this. You want a factory (which, of course, you can build using your container). The factory would know how to take, say, an IProcessorSettings
and return the appropriate IProcessor
. In short, you can build a factory that uses the concrete type of an object that implements IProcessorSettings
and the container to resolve an instance of the appropriate type.
I think what you are looking for is the ForObject()
method in StructureMap. It can close an open generic type based on a given object instance. The key change you need to make to your design is to introduce the generic type:
public interface IProcessor { }
public interface IProcessor<TSettings> : IProcessor{}
All of the important stuff is still declared on IProcessor
, the generic IProcessor<TSettings>
is really just a marker interface. Each of your processors will then implement the generic interface, to declare which settings type they expect:
public class FooProcessor : IProcessor<FooSettings>
{
public FooProcessor(FooSettings settings) { }
}
public class BarProcessor : IProcessor<BarSettings>
{
public BarProcessor(BarSettings settings) { }
}
public class DohProcessor : IProcessor<DohSettings>
{
public DohProcessor(DohSettings settings) { }
}
Now, given an instance of a settings object, you can retrieve the correct IProcessor
:
IProcessor processor = container.ForObject(settings).
GetClosedTypeOf(typeof(IProcessor<>)).
As<IProcessor>();
Now you can tell StructureMap to use this logic whenever it resolves an IProcessor
:
var container = new Container(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.ConnectImplementationsToTypesClosing(typeof(IProcessor<>));
});
x.For<IProcessor>().Use(context =>
{
// Get the settings object somehow - I'll assume an ISettingsSource
var settings = context.GetInstance<ISettingsSource>().GetSettings();
// Need access to full container, since context interface does not expose ForObject
var me = context.GetInstance<IContainer>();
// Get the correct IProcessor based on the settings object
return me.ForObject(settings).
GetClosedTypeOf(typeof (IProcessor<>)).
As<IProcessor>();
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With