Castle Windsor: How to specify a constructor parameter from code?
Asked Answered
T

6

11

Say I have the following class

MyComponent : IMyComponent {
  public MyComponent(int start_at) {...}
}

I can register an instance of it with castle windsor via xml as follows

<component id="sample"  service="NS.IMyComponent, WindsorSample" type="NS.MyComponent, WindsorSample">  
  <parameters>  
    <start_at>1</start_at >  
  </parameters>  
</component>  

How would I go about doing the exact same thing but in code? (Notice, the constructor parameter)

Tekla answered 17/9, 2008 at 21:23 Comment(0)
C
16

Edit: Used the answers below code with the Fluent Interface :)

namespace WindsorSample
{
    using Castle.MicroKernel.Registration;
    using Castle.Windsor;
    using NUnit.Framework;
    using NUnit.Framework.SyntaxHelpers;

    public class MyComponent : IMyComponent
    {
        public MyComponent(int start_at)
        {
            this.Value = start_at;
        }

        public int Value { get; private set; }
    }

    public interface IMyComponent
    {
        int Value { get; }
    }

    [TestFixture]
    public class ConcreteImplFixture
    {
        [Test]
        void ResolvingConcreteImplShouldInitialiseValue()
        {
            IWindsorContainer container = new WindsorContainer();

            container.Register(
                Component.For<IMyComponent>()
                .ImplementedBy<MyComponent>()
                .Parameters(Parameter.ForKey("start_at").Eq("1")));

            Assert.That(container.Resolve<IMyComponent>().Value, Is.EqualTo(1));
        }

    }
}
Catching answered 18/9, 2008 at 14:59 Comment(3)
Does this solution work if the parameter is a complex type, such as another IMyComponent?Pruett
If the dependency is in the container it will automatically be resolvedCatching
I'd love to use a fluent interface, however downloading castle source, nant (which I've never used before) and figuring it all out is a bit muchTekla
B
2

Try this

int start_at = 1; 
container.Register(Component.For().DependsOn(dependency: Dependency.OnValue(start_at)));
Barbarism answered 14/10, 2014 at 14:30 Comment(2)
can you explain a little further?Vilipend
This is an honest attempt at an answer and so shouldn't be flagged as not an answer. If you don't like the answer, you should downvote it, not flag it.Le
B
1

Have you considered using Binsor to configure your container? Rather than verbose and clumsy XML you can configure Windsor using a Boo based DSL. Here's what your config will look like:

component IMyComponent, MyComponent:
   start_at = 1

The advantage is that you have a malleable config file but avoid the problems with XML. Also you don't have to recompile to change your config as you would if you configured the container in code.

There's also plenty of helper methods that enable zero friction configuration:

  for type in Assembly.Load("MyApp").GetTypes():
    continue unless type.NameSpace == "MyApp.Services"
    continue if type.IsInterface or type.IsAbstract or type.GetInterfaces().Length == 0
    component type.GetInterfaces()[0], type

You can get started with it here.

Branham answered 7/11, 2008 at 1:12 Comment(1)
When I get more than 3 seconds to myself I plan to take a look at it. It is also important for me that this be something I can change without recompiling since I plan to turn on/off interceptors to debug apps in the fieldTekla
B
0

You need to pass in an IDictionary when you ask the container for the instance.

You'd use this Resolve overload of the IWindsorContainer:

T Resolve<T>(IDictionary arguments)

or the non generic one:

object Resolve(Type service, IDictionary arguments)

So, for example: (assuming container is an IWindsorContainer)

IDictionary<string, object> values = new Dictionary<string, object>();
values["start_at"] = 1;
container.Resolve<IMyComponent>(values);

Note that the key values in the dictionary are case sensitive.

Bernardinebernardo answered 17/9, 2008 at 21:32 Comment(2)
Sorry Gareth, but this is not the same thing as the XML I posted. Here the parameter needs to be known by the resolver whereas in question the XML provides a default value.Tekla
+1 for solving a different problem - instantiating an object with a parameter passed at runtime.Lakendra
S
0

You could use a configuration class to read the app.config. Then register that and get windsor to use it for its dependency. Ideally my MyConfiguration would use an interface.

public class MyConfiguration
{
    public long CacheSize { get; }

    public MyConfiguration()
    {
        CacheSize = ConfigurationManager.AppSettings["cachesize"].ToLong();
    }
}



container.Register(Component.For<MyConfiguration>().ImplementedBy<MyConfiguration>());

container.Register(Component.For<MostRecentlyUsedSet<long>>()
.ImplementedBy<MostRecentlyUsedSet<long>>().
DependsOn(Dependency.OnValue("size", container.Resolve<MyConfiguration>().CacheSize))
.LifestyleSingleton());
Smelter answered 17/4, 2019 at 16:15 Comment(0)
S
-1

You can use the AddComponentWithProperties method of the IWindsorContainer interface to register a service with extended properties.

Below is a 'working' sample of doing this with an NUnit Unit Test.

namespace WindsorSample
{
    public class MyComponent : IMyComponent
    {
        public MyComponent(int start_at)
        {
            this.Value = start_at;
        }

        public int Value { get; private set; }
    }

    public interface IMyComponent
    {
        int Value { get; }
    }

    [TestFixture]
    public class ConcreteImplFixture
    {
        [Test]
        void ResolvingConcreteImplShouldInitialiseValue()
        {
            IWindsorContainer container = new WindsorContainer();
            IDictionary parameters = new Hashtable {{"start_at", 1}};

            container.AddComponentWithProperties("concrete", typeof(IMyComponent), typeof(MyComponent), parameters);

            IMyComponent resolvedComp = container.Resolve<IMyComponent>();

            Assert.That(resolvedComp.Value, Is.EqualTo(1));
        }

    }
}
Summers answered 18/9, 2008 at 15:11 Comment(1)
Just got around to trying this, it doesn't work: Could not resolve non-optional dependency for 'concrete' (WindsorSample.MyComponent). Parameter 'start_at' type 'System.Int32'Tekla

© 2022 - 2024 — McMap. All rights reserved.