see @samy answer, but it was the open class that was incorrect. I just need to change:
StockDelayModule<T> : ModulePartBase<T>, IModulePart<T> where T : StockDelay
to
StockDelayModule : ModulePartBase<StockDelay>, IModulePart<StockDelay>
then all references to T within StockDelayModule
to StockDelay
The AsFactory()
registration then works fine and can resolve the correct component.
I've been reluctant to post this question as I wanted to figure it out, but I also don't want a barrage of "duplicate" comments, but I'm at a loss.
I've got an interface
public interface IModulePart<T>
{
IResponse<T> Create(CreateRequest request, IPromise<T> promise);
IResponse Delete(DeleteRequest request, IPromise<T> promise);
IResponse<T> Read(ReadRequest request, IPromise<T> promise);
IResponse<T> Update(UpdateRequest request, IPromise<T> promise);
}
with several implementations which vary based on T
. For instance:
WebUrlModule<T> : IModulePart<T> where T : WebUrl
StockDelayModule<T> : IModulePart<T> where T : StockDelay
I've got my installer, which is registering the components fine:
Classes.FromAssemblyInDirectory(new AssemblyFilter("bin"))
.BasedOn(typeof(IModulePart<>))
.WithService.Base()
.LifestyleTransient()
I've got my factory:
public interface IModulePartFactory
{
IModulePart<T> FindModulePartFor<T>();
}
There is then my factory selector which is running, overriding GetComponentType
from DefaultTypedFactoryComponentSelector
:
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
if (method.Name == "FindModulePartFor")
{
return typeof(IModulePart<>).MakeGenericType(method.GetGenericArguments()[0]);
}
return base.GetComponentType(method, arguments);
}
Every thing seems to look mostly correct, and when debugging (using WebUrl
) the generic type is something like IModulePart'1[[WebUrl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
However, as you may have guessed, it isn't working. If I make a call with StockDelay
, it resolves fine because I think it's returning the first registered component, but when using WebUrl
, it breaks. I say StockDelay
works, but I think it's more just the case that it's returning anything (first matching component), rather than selecting the correct component.
The message I get is:
Types WebUrl don't satisfy generic constraints of implementation type StockDelayModule'1 of component 'StockDelayModule'1'. This is most likely a bug in your code.
Anything glaringly obvious to anyone, or suggestions as to anything that might be incorrect? Or am I just trying something that isn't possible!? Thanks.
If I change my classes to abstract and implement them with a derived class that isn't generic it works. So it leads me to think it's just a small configuration in castle related to the generic that's meaning it isn't resolving correctly. Ideas?:
public class StockDelayModule : StockDelayModulePart<StockDelay> { }
public abstract class StockDelayModulePart<T> : ModulePartBase<T>, IModulePart<T> { }
You don't need the custom resolver, because Castle will try to resolve the factory call depending on the return type. Unless there are some other things you need, the following should be sufficient to resolve a IModulePart<T>
depending on T
:
public class WebUrl { }
public class StockDelay { }
public interface IModulePart<T> { }
public class WebUrlModule : IModulePart<WebUrl> { }
public class StockDelayModule : IModulePart<StockDelay> { }
public interface IModulePartFactory
{
IModulePart<T> FindModulePartFor<T>();
}
internal class Program
{
private static void Main(string[] args)
{
var container = new Castle.Windsor.WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Classes.FromAssemblyInThisApplication()
.BasedOn(typeof(IModulePart<>))
.WithService.Base()
.LifestyleTransient(),
Component.For<IModulePartFactory>().AsFactory()
);
var factory = container.Resolve<IModulePartFactory>();
var moduleForWebUrl = factory.FindModulePartFor<WebUrl>();
// type is WebUrlModule
var moduleForStockDelay = factory.FindModulePartFor<StockDelay>();
// type is StockDelayModule
}
}