dan richardson
dan richardson

Reputation: 3939

Picking the correct component from a generic typed factory

Solution

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.


Update

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> { }

Upvotes: 1

Views: 729

Answers (2)

samy
samy

Reputation: 14972

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
    }
}

Upvotes: 1

Ognyan Dimitrov
Ognyan Dimitrov

Reputation: 6273

What happens if you try to override the other method :

protected override string GetComponentName(MethodInfo method, object[] arguments)

What the container registrations say? You can check the registration and the section with the misconfigured components.

Although overriding this method may partially solve the problem it does not solve the root of the problem - it is another way to check if you have misconfigured your component.

Upvotes: 0

Related Questions