Tom
Tom

Reputation: 16276

passing parameters to Castle Windsor Typed Factory

I am following this document to use typed factory and pass parameters into constructor. The typed factory is giving me this error when I try to pass 2 parameters (1,"fo") as shown in the code.

enter image description here

public class SomeClass {
    public ITypedFactory2 F2 { get; set; } 
    public void SomeFunction() {
        var req = F2.Create<IGetFooRequest>(1, "fo"); // ERROR HERE
    }
}
public class GetFooRequest : IGetFooRequest {
    public int Bar { get; private set; }
    public string Ton { get; private set; }
    public GetFooRequest(int bar, string ton ) {
        Bar = bar;
        Ton = ton;
    }
}
public interface IGetFooRequest{
    int Bar { get; }
    string Ton { get; }
}    
public interface ITypedFactory2 {
    T Create<T>(int param1, string param2);
    void Release(object t);
}

and this is the windsor installer part...

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<ITypedFactory2>().AsFactory());
container.Register(AllTypes
            .FromAssemblyContaining<IGetFooRequest>()
            .Where(type => type.Name.EndsWith("Request"))
            .WithService.AllInterfaces().LifestyleTransient());

Why it says could not resolve non-optional dependency...? I have passed in (1,"fo"); I really don't understand why this is happening... Please help.

Upvotes: 3

Views: 4396

Answers (3)

SimonG
SimonG

Reputation: 322

If you want to resolve by Type over parameter name you can create your own ComponentSelector:

public class ComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override Arguments GetArguments(MethodInfo method, object[] arguments)
    {
        if (arguments == null)
            return null;

        Arguments argumentMap = new Arguments();
        ParameterInfo[] parameters = method.GetParameters();

        List<Type> types = parameters.Select(p => p.ParameterType).ToList();
        List<Type> duplicateTypes = types.Where(t => types.Count(type => type == t) > 1).ToList();

        for (int i = 0; i < parameters.Length; i++)
        {
            if (duplicateTypes.Contains(parameters[i].ParameterType))
                argumentMap.Add(parameters[i].Name, arguments[i]);
            else
                argumentMap.Add(parameters[i].ParameterType, arguments[i]);
        }

        return argumentMap;
    }
}

As you can see in my implementation, you need to handle the case when your constructor has multiple parameters with the same type.
In that case you have to resolve by parameter name because Castle.Windsor will use the first parameter of a type for every parameter that has the same type.

To use your own ComponentSelector you have to register it with your IWindsorContainer as well:

container.Register(Component.For<ComponentSelector, ITypedFactoryComponentSelector>());

Finally you have to tell your factory to use your own ComponentSelector as its component selector:

container.Register(Component.For<ITypedFactory2>().AsFactory(f => f.SelectedWith<ComponentSelector>()));

For more information check the official documentation on how to use custom component selectors.

Upvotes: 1

Wabble
Wabble

Reputation: 96

I had the same question and just figured out the answer. The parameters' names of your factory method and those of your class constructor must match, case insensitive.

so change your factory interface to

public interface ITypedFactory2 {
    T Create<T>(int **bar**, string **ton**);
    void Release(object t);
}

or your class to

public class GetFooRequest : IGetFooRequest {
    public int Bar { get; private set; }
    public string Ton { get; private set; }
    public GetFooRequest(int **param1**, string **param2**) {
        Bar = bar;
        Ton = ton;
    }
}

Upvotes: 8

Tom
Tom

Reputation: 16276

I looked at my own code and say, (int param1, string param2) doesn't look good. Let me use (int bar, string ton) ... and that naming fixed the problem. LoL Unbelievable, and I don't see that document mentioned the significance of naming.

Luckily I do remember the intro here says dependencies are resolved first by name, then by type. So that is the by name part doing its work, and the by type part gone into water. Anyway, I am glad I figured out how to use it, so I share my answer here with whoever needs it.

Upvotes: 2

Related Questions