I have the following typed factory:
public interface IWorkerFactory
{
IWorker Create(IJob job);
void Release(IWorker handler);
}
This factory creates workers for a given job. The jobs all implement IJob
but I want a couple of components which handle jobs of different types:
e.g. both of these derive from IJob
interface IJobType1 : IJob { }
interface IJobType2 : IJob { }
And I want two worker types, one that deals with each type of IJob
class Worker1 : IWorker
{
public Worker1(IJobType1 job) { }
}
class Worker2 : IWorker
{
public Worker2(IJobType2 job) { }
}
I've tried this, but Windsor just picks the first implementation of IWorker
it finds every time, attempting to pass an IJobType1
to Worker2
(or vice-versa) which throws an exception
I've looked at the typed factory component selector but I can't quite work out how I would use this to the intended effect
Is this possible or is it better to attempt to use a generic interface for my worker classes?
Edit:
Tried using a generic interface but that doesn't seem to give me the API I'm looking for...the key issue is that I don't really want to know what type the IJob
is but I want the most appropriate IWorker
instance to be created at resolution time.
I'm calling it like this:
public void ProcessItem(IJob job)
{
IProcessingExceptionLog exLog = null;
IWorker worker = null;
try
{
worker = _workerFactory.Create(job);
// Attempt to process
worker.Process();
}
catch (Exception ex) .... etc
Even if I do cast job to a more derived interface, Windsor still picks the first IWorker...it's not interested in checking the parameters of the method that closely
I'd ideally like some way to intercept or customise the resolution process so that I can determine what the closest matching signature that accepts IJob
is.
I could try implementing a concrete factory based on the IWorkerFactory
interface myself. It could look at all implementors of IWorker
and determine which ones constructor matched the arguments closest, however, how do I resolve from the container in this instance as I won't have a container instance to use? I don't really like the idea of making the container static
Ok I figured this one out - I wanted two types of IWorker
but I didn't really want to use component naming. I ended up using component naming but based on an enum, then since this was already part of IJob
I used it as part of my component selector to determine which named component to use:
The enum:
public enum JobType
{
Type1,
Type2
}
Container installer:
container.Register(Component.For<IWorker>()
.ImplementedBy<Worker1>()
.Named(JobType.Type1.ToString()));
container.Register(Component.For<IWorker>()
.ImplementedBy<Worker2>()
.Named(JobType.Type2.ToString()));
// And the factory:
container.Register(Component.For<IWorkerFactory>()
.AsFactory(x => x.SelectedWith<WorkerFactorySelector>()));
Then in IJob
I actually already had the enum:
public interface IJob
{
JobType JobType { get; }
}
Then in WorkerFactorySelector
:
public class WorkerFactorySelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(System.Reflection.MethodInfo method, object[] arguments)
{
var job = arguments[0] as IJob;
if (job != null)
return job.JobType.ToString();
return null;
}
}
This way I don't need to lug around any constants and if I need new job types I can just stick them on the enum and have the jobs associate themselves automatically based on their implementation.
Update 04/05/18:
As Edu mentioned in his comment, you also need to register the selector with your container as it will use the container to resolve the selector.