Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving dynamically base on name convention in Castle Windsor

I have a seemingly simple use case. There is a ICsvReader component. Let's name it simply Reader here. We load a known set of CSV files and some of them have headers and some don't. Currently there are multiple readers: Reader_Skips1Row, Reader_Skips2Rows etc.

Is there a way to register only one component and have Windsor look at the component key, strip the "_Skips..." part and resolve the required component with relevant properties set?

I have tried subresolver and facility with no luck.

EDIT

Yes there is only one implementation but it is used as a dependency and configured to be resolved by name. The reader is configured in code

Component.For<ICsvReader>()
         .ImplementedBy<CommaSeparetedCsvReader>()
         .DependsOn(new { SkipHeader = true, HeaderRowsToSkip = 2 } )
         .Named("CommaSeparetedCsvReader_Skips2Rows")
         .Lifestyle.Transient

Component.For<ICsvReader>()
         .ImplementedBy<CommaSeparetedCsvReader>()
         .DependsOn(new { SkipHeader = true, HeaderRowsToSkip = 1 } )
         .Named("CommaSeparetedCsvReader_Skips1Row")
         .Lifestyle.Transient

Component.For<ICsvReader>()
         .ImplementedBy<CommaSeparetedCsvReader>()
         .Named("CommaSeparetedCsvReader")
         .Lifestyle.Transient

These are used as dependency in a processor class. It is configured in XML, so that in can be manipulated at runtime

<component id="Processor 
   type="Processor">
   <parameters>
      <reader>CommaSeparetedCsvReader_Skips2Rows</reader>
   </parameters>
</component>

Ideally I would like to register only the CommaSeparetedCsvReader component but when an attempt is made to resolve CommaSeparetedCsvReader_Skips2Rows it should strip the suffix, parse it and change the properties accordingly.

Is it possible to somehow modify the Resolve() behavior?

Thanks, Tom

like image 240
Tomasz Pluskiewicz Avatar asked Dec 21 '22 11:12

Tomasz Pluskiewicz


1 Answers

If you are resolving your components using the TypedFactoryFacility, creating a custom ITypedFactoryComponentSelectors might help you. I would need more detail on how you create the Readers to give you more info.

Kind regards, Marwijn.

Edit =====================================

Let's add an example:

public interface IFoo
{
}

public class Foo1 : IFoo
{
}

public class Foo2 : IFoo
{
}

public interface IFooFactory
{
    IFoo CreateFoo(string which);
}

public class FooFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return (string)arguments[0];
    }
}

--- registration

container.AddFacility<TypedFactoryFacility>();
Component.For<IFoo>().Named("Foo1Name").ImplementedBy<Foo1>(), 
Component.For<IFoo>().Named("Foo2Name").ImplementedBy<Foo2>(),
Component.For<IFooFactory>().AsFactory(f => f.SelectedWith(new FooFactoryComponentSelector())),

--- usage

var factory = _container.Resolve<IFooFactory>(); // in general this would just be a dependency in the constructor.
var foo = factory.CreateFoo("Foo2Name");

Just adapt the component selector to your needs. If necessary you can also pass additional arguments to CreateFoo, if the constructor requires arguments not provided by the container.

More info: http://docs.castleproject.org/Windsor.Typed-Factory-Facility-interface-based-factories.ashx

like image 64
Marwijn Avatar answered Apr 21 '23 18:04

Marwijn