Using Castle Windsor, I'd like to create a class that records an integer. But I'd like to decorate it several times with other classes. I can see how this works if all concretes involved have dependencies that can be resolved, but that's not the case here. Consider this code:
public interface IRecorder
{
void Add(int value);
}
public class NotifyingRecorder : IRecorder
{
readonly IRecorder decoratedRecorder;
public NotifyingRecorder(IRecorder decoratedRecorder)
{
this.decoratedRecorder = decoratedRecorder;
}
public void Add(int value)
{
decoratedRecorder.Add(value);
System.Console.WriteLine("Added " + value);
}
}
public class ModelUpdatingRecorder : IRecorder
{
int seed;
public ModelUpdatingRecorder(int seed)
{
this.seed = seed;
}
public void Add(int value)
{
seed += value;
}
}
And registered with:
container.Register(Component.For<IRecorder>().ImplementedBy<NotifyingRecorder>());
container.Register(Component.For<IRecorder>().ImplementedBy<ModelUpdatingRecorder>());
Resolving an IRecorder will never work here, since ModelUpdatingRecorder has a non-optional dependency. I cannot use a static dependency since seed is not known at compile-time.
Is there a way to specify the seed parameter at runtime and have the decoration still work?
This code sample is a simplification of my scenario, but the idea is the same. I have decorators, and the lowest one relies on a specific value/instance to be provided to it.
I've found a solution that I believe is the way this should be done. Down in the innards of Windsor the DefaultDependencyResolver has a method it uses to resolve sub-dependencies (such as a decorated instance of IRecorder above) called RebuildContextForParameter. It calls this to create a new context to use when resolving the dependency (i.e. the parameter to the constructor). The method is:
/// <summary>This method rebuild the context for the parameter type. Naive implementation.</summary>
protected virtual CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
{
if (parameterType.ContainsGenericParameters)
{
return current;
}
return new CreationContext(parameterType, current, false);
}
The false parameter in CreationContext constructor is propagateInlineDependencies, which when true will copy over the current context's AdditionalArguments, thereby passing down the parameters to the sub-dependencies.
To flip this false to true, create a new class that derives from DefaultDependencyResolver:
public class DefaultDependencyResolverInheritContext : DefaultDependencyResolver
{
protected override CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
{
if (parameterType.ContainsGenericParameters)
{
return current;
}
return new CreationContext(parameterType, current, true);
}
}
Then use that when creating the Windsor container:
var kernel = new DefaultKernel(
new DefaultDependencyResolverInheritContext(),
new NotSupportedProxyFactory());
var container = new WindsorContainer(kernel, new DefaultComponentInstaller());
The NotSupportedProxyFactory and DefaultComponentInstaller are the defaults when using the parameter-less constructors for DefaultKernel and WindsorContainer.
When done, the code above will work when a factory is used to create an IRecorder, i.e.:
// during type registration/bootstrapping
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IRecorder>().ImplementedBy<NotifyingRecorder>());
container.Register(Component.For<IRecorder>().ImplementedBy<ModelUpdatingRecorder>());
container.Register(Component.For<IRecorderFactory>().AsFactory());
Where IRecorderFactory is:
public interface IRecorderFactory
{
IRecorder Create(int seed);
}
Then this will work as expected:
IRecorderFactory recorderFactory = container.Resolve<IRecorderFactory>();
IRecorder recorder = recorderFactory.Create(20);
recorder.Add(6);
Hopefully that helps others!
You could wrap the seed in an interface (ISeedHolder ?) and register it with a singleton lifestyle. Then use the interface in your ModelUpdatingRecorder instead of the raw int. Unless your seeds may need to be parallelized it should allow you to set the seed and resolve it when constructing the ModelUpdatingRecorder
public interface ISeedHolder
{
int Seed {get;set;}
}
public class ModelUpdatingRecorder : IRecorder
{
int seed;
public ModelUpdatingRecorder(ISeedHolder seedHolder)
{
this.seed = seedHolder.Seed;
}
Would this solution achieve what you need?
Related
In a .NET Core 3.1 console application I want a Class that would have some parameters in constructor injected but some that I could assign manually. For example something like that but with IConfiguration Injected:
static void Main() {
var myObj1 = new MyClass(1);
var myObj2 = new MyClass(2);
}
public class MyClass {
public MyClass(IConfiguraiton config, int myVal)
{
}
}
I tried this with Ninject:
static void Main()
{
kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
kernel.Get<MyClass>();
}
public class MyClass
{
public MyClass(IConfiguraiton config)
{
}
}
public class Bindings : NinjectModule
{
public override void Load()
{
var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.json").Build();
Bind<IConfiguration>().ToMethod(ctx => SetupConfiguration()).InSingletonScope();
Bind<MyClass>().ToSelf().InTransientScope();
}
}
I managed to make simple dependency injection, but haven't had any success making injection with parameters.
I've read a lot of people suggesting that it's better to pass parameters into methods of the class rather than constructor, however in my situation this isn't an option in addition I'm a software engineering student, and would like to learn how to do this, since it might be useful in some situations.
This is a situation where the Ninject.Extensions.Factory is useful, as it is made exactly for this situation. It does pull in the Factory dependency in addition to Castle.Core, as it uses DynamicProxy under the hood (as a SE student, playing with this library is a good idea for using the interceptor pattern).
To use it, you define a Factory interface like so:
public interface IMyClassFactory
{
MyClass Create(int myVal);
}
Note that the Create method returns MyClass, and the argument(s) to the Create method match exactly in type and name to the arguments you wish to provide. The argument type(s) you want injected must be registered with the kernel. Unfortunately, it is easy to make a mistake here - if you specify a parameter that does not exist in the factory interface it is ignored, but if you forget one it will throw an exception when called.
Next, register IMyClassFactory like this: Bind<IMyClassFactory>().ToFactory(); and remove your binding for MyClass. Then wherever you need to create an instance, inject IMyClassFactory and call Create: kernel.Get<IMyClassFactory>().Create(2)
You can achieve the same result without using Ninject.Extensions.Factory by writing and registering your own implementation of IMyClassFactory, essentially doing the same thing that the code the Factory extension ends up emitting. A full sample is below using both methods based on commenting in/out the registration (note the output if you add .InSingletonScope() to the registration of IConfiguraiton - both approaches respect the binding scopes of Ninject).
internal class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Bind<IConfiguraiton>().To<Configuraiton>();
kernel.Bind<IMyClassFactory>().ToFactory();
//kernel.Bind<IMyClassFactory>().To<NinjectMyClassFactory>().InSingletonScope();
var factory = kernel.Get<IMyClassFactory>();
var one = factory.Create(1);
var two = factory.Create(2);
}
}
public interface IMyClassFactory
{
MyClass Create(int myVal);
}
public class NinjectMyClassFactory : IMyClassFactory
{
public NinjectMyClassFactory(IResolutionRoot resolutionRoot)
{
ResolutionRoot = resolutionRoot;
}
private IResolutionRoot ResolutionRoot { get; }
public MyClass Create(int myVal)
{
return ResolutionRoot.Get<MyClass>(new ConstructorArgument("myVal", myVal));
}
}
public class MyClass
{
public MyClass(IConfiguraiton config, int myVal)
{
Console.Out.WriteLine("Created MyClass({0},{1})", config.MyNum, myVal);
}
}
public interface IConfiguraiton { int MyNum { get; } }
public class Configuraiton : IConfiguraiton
{
static int CreateCount;
public Configuraiton()
{
MyNum = Interlocked.Increment(ref CreateCount);
}
public int MyNum { get; }
}
I'm trying to use Automapper projections on Entity Framework IQueryables.
On application start, I create and add all my mapping profiles which create maps with the non-static CreateMap method.
All those profiles are registered within my IoC container.
I get the missing mapping exception although I see the mapping profile in the instance of my mappingConfiguration.
What could be the problem? Am I missing something? I'm using Automapper 4.2.1
I've noticed that when adding a static Mapper.CreateMap, it works fine. Do projections work only with static API? I want to avoid the static API.
Full code:
public class ItemEntityToItemView : Profile
{
public override void Configure()
{
CreateMap<ItemEntity, ItemView>();
// Without this line, I get missing Map type configuration.
Mapper.CreateMap<ItemEntity, ItemView>();
}
}
public interface IEntitiesProjector
{
IQueryable<T> SelectTo<T>(IQueryable source);
}
public class EntitiesProjector : IEntitiesProjector
{
private readonly IMapperConfiguration _mapperConfig;
public EntitiesProject(IMapperConfiguration mapperConfig)
{
_mapperConfig = mapperConfig;
}
public IQueryable<T> SelectTo<T>(IQueryable source)
{
return source.ProjectTo<T>(_mapperConfig);
}
}
public class ItemsRepository : IITemsRepository
{
public IQueryable<ItemEntity> GetById(int id)
{
return _dbSet.Where(x => x.Id == id);
}
}
public class Service
{
private readonly IEntitiesProjector _projector;
public Service(IEntitiesProject entitiesProjector)
{
_projector = entitiesProjector;
}
public List<T> GetItem(int id)
{
IQueryable<ItemEntity> itemsQueryable = ItemsRepository.GetById(id);
return _projector.SelectTo<ItemView>(itemsQueryable);
}
}
My Autofac registration :
builder.RegisterAssemblyTypes().AssignableTo(typeof(Profile)).As<Profile>();
builder.Register(c => new MapperConfiguration(cfg =>
{
cfg.CreateMap<IdentityUser, AspNetUser>().ReverseMap();
})).AsSelf().As<IMapperConfiguration>().SingleInstance();
builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve)).As<IMapper>().InstancePerLifetimeScope();
builder.Register<EntitiesProjector>().As<IEntitiesProjector>().SingleInstance();
The reason is the following block:
public class EntitiesProjector : IEntitiesProjector
{
private readonly IMapperConfiguration _mapperConfig;
public EntitiesProject(IMapperConfiguration mapperConfig)
{
_mapperConfig = mapperConfig;
}
public IQueryable<T> SelectTo<T>(IQueryable source)
{
return source.ProjectTo<T>(_mapperConfig);
}
}
source.ProjectTo is an extension method which has 5 overloads. In documentation they are passing instance of MappingConfiguration class there, and you are passing instance of IMapperConfiguration (interface). You think it will have the same effect, but it does not. IMapperConfiguration interface does not implement IConfigurationProvider interface, and that (IConfigurationProvider) is what correct overload of ProjectTo accepts. But, there is another overload of ProjectTo, which accepts "object parameters". Because it accepts object - it will match anything which did not fit other overloads. So what you are really calling is ProjectTo(object) overload, which has nothing to do with configuration, and your IMapperConfiguration together with profiles and maps is completely ignored.
Quickfix will be
public class EntitiesProjector : IEntitiesProjector
{
private readonly IConfigurationProvider _mapperConfig;
public EntitiesProjector(IMapperConfiguration mapperConfig)
{
_mapperConfig = (IConfigurationProvider)mapperConfig;
}
public IQueryable<T> SelectTo<T>(IQueryable source)
{
return source.ProjectTo<T>(_mapperConfig);
}
}
But of course you should better register your configuration as IConfigurationProvider in your container, that is just quick fix to ensure problem is really here.
As for static Mapper.CreateMap - well it's static, so works regardless of what you pass to ProjectTo.
As a side note this shows you how to not design api. Whenever you have many overloads and one of them accepts generic object and does completely different thing than all other overloads - that is asking for trouble.
My factory is using method injection because I thought this was the best way to make it so far. Besides, I doubt it is a good thing after having to call on its Create method from within a dependent object.
The only way I might think of whilst continuing to use the parameterized factory Create method, is to inject the dependencies directly in the MainPresenter so that it may provide with the dependencies to the method, and I dislike it. It dislike it because it is not the MainPresenter that depends on the ICustomerManagementView and the ICustomerDetailPresenterFactory, it's its dependency. So I would feel like I'm sabotaging my own code by doing so.
MainPresenter
public class MainPresenter : Presenter<IMainView>, IMainViewUiHandler {
public MainPresenter(IMainView view
, ICustomerManagementPresenterFactory customerManagementFactory)
: base(view) {
this.customerManagementPresenterFactory = customerManagementPresenterFactory;
}
public void ManageCustomers() {
// The following line is causing trouble.
// As you can see per the ICustomerManagementPresenterFactory code sample,
// the Create() method takes two parameters:
// 1. ICustomerManagementView, and
// 2. ICustomerDetailPresenterFactory
// Hence I have to provide the dependencies manually, I guess. Which is
// something to avoid at any cost.
var customerManagementPresenter = customerManagementPresenterFactory.Create();
customerManagementPresenter.ShowView();
}
}
ICustomerManagementPresenterFactory
public interface ICustomerManagementPresenterFactory {
// Here. Though I ask Ninject to inject my dependencies, I need to
// provide values to the parameters when calling the method from within
// the MainPresenter class. The compiler won't let me do otherwise! And
// this makes sense!...
[Inject]
CustomerManagementPresenter Create(ICustomerManagementView view
, ICustomerDetailPresenterFactory factory);
}
IMainView
public interface IMainView : IView, IHasUiHandler<IMainViewUiHandler> {
}
IMainViewUiHandler
public interface IMainViewUiHandler : IUiHandler {
void ManageCustomers();
}
IUiHandler
public interface IUiHandler {
}
IHasUiHandler
public interface IHasUiHandler<H> where H : IUiHandler {
H Handler { set; }
}
MainForm
public partial class MainForm : Form, IMainView {
public MainForm() { InitializeComponent(); }
public IMainViewUiHandler Handler { private get { return handler; } set { setHandler(value); } }
}
CompositionRoot
public class CompositionRoot {
private CompositionRoot() { }
public static IKernel BuildObjectGraph() {
IKernel kernel = new StandardKernel();
BindFactories(kernel);
BindViews(kernel);
}
private static void BindFactories(IKernel kernel) {
kernel.Bind(services => services
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => !a.FullName.Contains("Tests")))
.SelectAllInterfaces()
.EndingWith("Factory")
.BindToFactory()
);
}
private static void BindViews(IKernel kernel) {
kernel.Bind(services => services
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.FullName.Contains("Windows")
&& !a.FullName.Contains("Tests"))
.SelectAllClasses()
.EndingWith("Form")
.BindSelection((type, baseType) => type
.GetInterfaces()
.Where(iface => iface.Name.EndsWith("View"))
)
);
}
}
So I wonder, is it best to implement the ICustomerManagementPresenterFactory and bind the implementer with it within my CompositionRoot, so that I could provide those dependencies through constructor injection to the Create method which shall no longer take any arguments, or shall I make it otherwise?
What I like of writing a simple interface is that Ninject does it all for me to a factory, and no code is necessary to build an instance of the desired type. Besides, when the constructor of the class to be created uses constructor injection, it seems like it is impossible to have a simple factory interface bound as a factory, and one need to implement the factory interface by hand.
What did I get right/wrong?
As a matter of fact you don't need to pass parameters to the factory Create method at all - except in case they are parameters which need to be passed "down" because they cannot be bound in the composition root (for example an input value). However, passing such parameters to constructors is usually a code smell. Mostly, it's a better idea to pass these parameters to methods instead of constructors (for example: Adder.Add(5,3);, not new Adder(5, 3).ComputeResult();.
Now consider the following example, which works perfectly fine:
public class Dependency1 { }
public interface IDependency2 { }
public class Dependency2 : IDependency2 { }
public interface IBar { }
public class Bar : IBar
{
public Bar(Dependency1 d1, IDependency2 d2) { }
}
public interface IBarFactory
{
IBar Create();
}
var kernel = new StandardKernel();
kernel.Bind<IBarFactory>().ToFactory();
kernel.Bind<IBar>().To<Bar>();
kernel.Bind<Dependency1>().ToSelf();
kernel.Bind<IDependency2>().To<Dependency2>();
var factory = kernel.Get<IBarFactory>();
var bar = factory.Create();
bar.Should().BeOfType<Bar>();
even though Bar takes two constructor arguments, the generated IBarFactory's Create() method does not specify so. No problem, ninject will resolve it automatically.
Now let me give you an example what .ToFactory() actually results in. Consider the factory:
public interface ISomeFactory
{
ISomething Create(string parameter1);
}
Will result in (remark: it's realised by interceptors and not by weaving it, so the example is a simplificiation):
public class SomeFactory : ISomeFactory
{
private readonly IResolutionRoot resolutionRoot;
public SomeFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public ISomething Create(string parameter1)
{
this.resolutionRoot.Get<ISomething>(new ConstructorArgument("parameter1", parameter1);
}
}
The ConstructorArgument tells ninject to pass the value of parameter1 to the ctor-parameter named "parameter".
All other parameters are resolved "as usual". If a constructor parameter cannot be resolved (neither passed as parameter nor bound) ninject will throw an exception stating that the parameter cannot be resolved.
I am doing some research on design pattern implementation variants, i have come across and read some examples implemented here http://www.codeproject.com/Articles/37547/Exploring-Factory-Pattern and http://www.oodesign.com/factory-pattern.html. My focus of concern is when implementing factory pattern without reflection . the stated articles said that we need to register objects not classes which seems fine and logical to me but when seeing the implementation i see the duplication of objects e.g in the code below
// Factory pattern method to create the product
public IRoomType CreateProduct(RoomTypes Roomtype)
{
IRoomType room = null;
if (registeredProducts.Contains(Roomtype))
{
room = (IRoomType)registeredProducts[Roomtype];
room.createProduct();
}
if (room == null) { return room; }
else { return null; }
}
// implementation of concrete product
class NonACRoom : IRoomType
{
public static void RegisterProduct()
{
RoomFactory.Instance().RegisterProduct(new NonACRoom(), RoomTypes.NonAcRoom);
}
public void getDetails()
{
Console.WriteLine("I am an NON AC Room");
}
public IRoomType createProduct()
{
return new NonACRoom();
}
}
the method RegisterProduct is used for self registeration, we have to call it anyways before creating factory object i.e before some where in the main class of the client or anywhere applicable that ensure its calling. below is we are creating a new product and in the method above we are creating again a new product which seems non sense. any body comment on that
I have done something similar to this in the past. This is essentially what I came up with (and also doing away with the whole "Type" enumeration):
public interface ICreator
{
IPart Create();
}
public interface IPart
{
// Part interface methods
}
// a sample creator/part
public PositionPartCreator : ICreator
{
public IPart Create() { return new PositionPart(); }
}
public PositionPart : IPart
{
// implementation
}
Now we have the factory itself:
public sealed class PartFactory
{
private Dictionary<Type, IPartCreator> creators_ = new Dictionary<Type, IPartCreator>();
// registration (note, we use the type system!)
public void RegisterCreator<T>(IPartCreator creator) where T : IPart
{
creators_[typeof(T)] = creator;
}
public T CreatePart<T>() where T: IPart
{
if(creators_.ContainsKey(typeof(T))
return creators_[typeof(T)].Create();
return default(T);
}
}
This essentially does away with the need for a "type" enumeration, and makes things really easy to work with:
PartFactory factory = new PartFactory();
factory.RegisterCreator<PositionPart>(new PositionPartCreator());
// all your other registrations
// ... later
IPart p = factory.CreatePart<PositionPart>();
The first creation is used to give something to work on to RegisterProduct. Probably, the cost of that object is neglectable. It's done during initialization and won't matter much.
This instance is required though because in C# you need an object to call createProduct on. This is because you can't use reflection to store a reference to a type instead of a reference to an object.
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>();
});
});
StructureMap containers expose the Model property which allows you to query for the instances it contains.
var container = new Container(x =>
{
x.For<IProcessorConsumer>().Use<ProcessorConsumer>();
x.For<IProcessor>().Use(context =>
{
var model = context.GetInstance<IContainer>().Model;
if (model.PluginTypes.Any(t => typeof(FooSettings).Equals(t.PluginType)))
{
return context.GetInstance<FooProcessor>();
}
return context.GetInstance<BarProcessor>();
});
});
In Autofac given:
public class AcceptsTypeConstructorFinder
: IConstructorFinder
{
private readonly Type m_typeToAccept;
public AcceptsTypeConstructorFinder(Type typeToAccept)
{
if (typeToAccept == null) { throw new ArgumentNullException("typeToAccept"); }
m_typeToAccept = typeToAccept;
}
public IEnumerable<ConstructorInfo> FindConstructors(Type targetType)
{
return targetType.GetConstructors()
.Where(constructorInfo => constructorInfo.GetParameters()
.Select(parameterInfo => parameterInfo.ParameterType)
.Contains(m_typeToAccept));
}
}
the following works:
// Load
var settings = new BarSettings();
var expectedProcessorType = typeof(BarProcessor);
// Register
var constructorFinder = new AcceptsTypeConstructorFinder(settings.GetType());
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterInstance(settings);
builder.RegisterAssemblyTypes(assembly)
.Where(type => type.IsAssignableTo<IProcessor>() && constructorFinder.FindConstructors(type).Any())
.As<IProcessor>();
builder.RegisterAssemblyTypes(assembly)
.As<IProcessorConsumer>();
using (var container = builder.Build())
{
// Resolve
var processorConsumer = container.Resolve<IProcessorConsumer>();
Assert.IsInstanceOfType(processorConsumer, typeof(ProcessorConsumer));
Assert.IsInstanceOfType(processorConsumer.Processor, expectedProcessorType);
// Run
// TODO
}
However, I find this to be rather cumbersome and was hoping for something more built into an IoC container.
Now, I'm not saying this is the right way to do this. However, it may be another option if you are using Autofac. This assumes that you are happy for the registration delegate to call GetSettings at the point you try and resolve the IProcessorConsumer. If you are able to do that, you can do what you want in the registration, as below:
var cb = new ConatainerBuilder();
cb.Register(c =>
{
var settings = GetSettings();
if(settings is FooSettings)
return new FooProcessor((FooSettings)settings);
else if(settings is BarSettings)
return new BarProcessor((BarSettings)settings);
else
throw new NotImplementedException("Hmmm. Got some new fangled settings.");
}).As<IProcessor>();
//Also need to register IProcessorConsumer
Note: This code may be wrong as I can't try it right now.
Here is as close as you can get to a proper factory method. But there are some issues. First, here's the code; then we'll talk.
public class FooSettings
{
public int FooNumber { get; set; }
public string FooString { get; set; }
}
public class BarSettings
{
public int BarNumber { get; set; }
public string BarString { get; set; }
}
public interface IProcessor
{
void Process();
}
public class FooProcessor : IProcessor
{
public FooProcessor(FooSettings settings) { }
public void Process() { }
}
public class BarProcessor : IProcessor
{
public BarProcessor(BarSettings settings) { }
public void Process() { }
}
public interface IProcessorFactory
{
IProcessor GetProcessor(object settings);
}
public interface IProcessorConsumer { }
public class ProcessorConsumer : IProcessorConsumer
{
private IProcessorFactory _processorFactory;
private object _settings;
public ProcessorConsumer(IProcessorFactory processorFactory, object settings)
{
_processorFactory = processorFactory;
_settings = settings;
}
public void MyLogic()
{
IProcessor processor = _processorFactory.GetProcessor(_settings);
processor.Process();
}
}
public class ExampleProcessorFactory : IProcessorFactory
{
public IProcessor GetProcessor(object settings)
{
IProcessor p = null;
if (settings is BarSettings)
{
p = new BarProcessor(settings as BarSettings);
}
else if (settings is FooSettings)
{
p = new FooProcessor(settings as FooSettings);
}
return p;
}
}
So what's the issue? It's the different types of settings that you're giving to your factory method. Sometimes it's FooSettings and sometimes it's BarSettings. Later, it might be xxxSettings. Each new type will force a recompilation. If you had a common Settings class, this would not be the case.
The other issue? Your consumer gets passed the factory and the settings and uses that to get the correct processor. If you have someone passing these to your consumer, just have that entity call GetProcessor on the Factory and pass the resulting IProcessor to the consumer.
I think the issue is that you haven't actually specified in any structured way how to resolve a processor from a settings instance. How about making the settings object return the processor? That seems to be the right place to put this information:
public interface ISettings
{
IProcessor GetProcessor();
}
Each implementation must resolve its own processor implementation:
public class FooSettings : ISettings
{
//this is where you are linking the settings type to its processor type
public IProcessor GetProcessor() { return new FooProcessor(this); }
}
And any code needing a processor gets it from the settings object, which you can reference from the consumer constructor:
var consumer = new ProcessorConsumer(Settings.GetProcessor());