... Home Contact

Krzysztof Koźmic's blog

Designed in Poland, assembled in Australia.


Show appreciation: My Amazon.com Wish List


Me@Twitter

    Currently reading

    Article Categories

    Archives

    Post Categories

    MyPersonal

    Syndication:

    Castle Windsor new feature – dynamic parameters from registration site

    UPDATE:

    I renamed the method from WithParameters to DynamicParameters to avoid confusion and be consistent with Parameters method which is used for static parameters.

    I just committed small new feature to Castle Windsor, that I think can nicely clean up your code. It remedies the following problem:

    Problem

    What if you have a component that relies on a dynamically provided primitive value? Things like current request’s Uri, or DateTime.Now?

    public class UsesCurrentTime
    {
       UsesCurrentTime(DateTime currentTime)
       {
          Console.WriteLine("Current time is {0}”, currentTime);
       }
    }

    Solution – the old way

    Until now you had two choices:

    • wrap the dependency in a service, like ICurrentUriProvider, or IClock and take dependency on that interface
      public class UsesCurrentTime
      {
         UsesCurrentTime(IClock currentTime)
         {
            Console.WriteLine("Current time is {0}", currentTime.Now);
         }
      }
    • in the code using that service, reference the container and pull the service yourself, instead of having it injected.
      public class Uses_UsesCurrentTime
      {
         private IKernel container;
       
         public void DoSomethingWithCurrentTime()
         {
            var service = container.Resolve<UsesCurrentTime>(new {currentTime = DateTime.Now});
            // do something with the service here
         }
      }

    Neither of these approaches is ideal. The new feature I mentioned is here to save the day. It basically lets you take the best of both approaches, without paying their price. You get to pass the parameters without additional layer of indirection, and without referencing container and pulling the service manually.

    Solution – the new way

    To use it, we need to use a new method on the fluent registration API: DynamicParameters, like this.

    kernel.Register(
        Component.For<UsesCurrentTime>()
            .LifeStyle.Transient
            .DynamicParameters((kernel, parameters) => parameters["currentTime"] = DateTime.Now));

    the method takes a delegate that will be invoked when the component is being resolved, but before any actual resolution, parameter matching, appropriate constructor searching etc happens. It gives you access to two things: the container itself, and the dictionary of parameters, that were passed from the call site. You can then inspect the parameters, add new, remove them, etc.

    What’s more interesting – you can use it not only for primitives. You can also dynamically override components.

    kernel.Register(
        Component.For<ICustomer>()
            .ImplementedBy<CustomerImpl>()
            .Named("defaultCustomer"),
        Component.For<ICustomer>().ImplementedBy<CustomerImpl2>()
            .Named("otherCustomer")
            .Parameters(
                Parameter.ForKey("name").Eq("foo"), // static parameters, resolved at registration time
                Parameter.ForKey("address").Eq("bar st 13"),
                Parameter.ForKey("age").Eq("5")),
        Component.For<CommonImplWithDependancy>()
            .LifeStyle.Transient
            .DynamicParameters((k, d) => // dynamic parameters
            {
                var randomNumber = 2;
                if (randomNumber == 2)
                {
                    d["customer"] = k.Resolve<ICustomer>("otherCustomer");
                }
            }));
     
    var component = kernel.Resolve<CommonImplWithDependancy>();
    Assert.IsInstanceOf<CustomerImpl2>(component.Customer);

    This test will pass.

    Technorati Tags:

    Feedback

    Gravatar

    # re: Castle Windsor new feature – dynamic parameters from registration site
    Jan | 12/12/2009 7:49 AM

    looks über cool!
    I wonder though how you would get to the current request`s uri...
    Gravatar

    # re: Castle Windsor new feature – dynamic parameters from registration site
    Krzysztof Koźmic | 12/12/2009 7:52 AM

    @Jan

    HttpContext.Current.Request.Url;

    Am I missing something here?
    Gravatar

    # re: Castle Windsor new feature – dynamic parameters from registration site
    Brad Mead | 1/28/2010 11:08 PM

    Krzysztof,

    I am looking for the Windsor equivalent for StructureMap's EnrichWith(..) convention. It was touched upon here:

    http://tunatoksoz.com/post/Implementing-EnrichWith(of-StructureMap)-with-Castle.aspx

    I am wondering. It looks like the delegate injection/interception here can be used to the same effect. Can you verify this. In other words, am I missing anything that would prevent a similar usage?

    Thanks,
    Brad
    Gravatar

    # re: Castle Windsor new feature – dynamic parameters from registration site
    Krzysztof Koźmic | 1/29/2010 9:20 PM

    Brad,

    OnCreate is the equivalent of EnrichWith. It's not core part of Windsor and does not requite any facility.

    DynamicParamteres serve different purpose.
    Gravatar

    # re: Castle Windsor new feature – dynamic parameters from registration site
    Brad Mead | 2/1/2010 7:48 PM

    Does the OnCreate interception allow injection of constructor arguments or only properties? The documentation shows only support Property assignment:

    kernel.Register(Component.For<MyComp>().OnCreate((kernel,item)=>item.Name="tuna"));

    Is there a way to set constructor arguments?
    Gravatar

    # re: Castle Windsor new feature – dynamic parameters from registration site
    Krzysztof Koźmic | 2/1/2010 8:41 PM

    Brad,

    OnCreate executes after object is created.

    If you want to affect its constructor arguments, you should use DynamicParameters or UsingFactoryMethod

    Comments have been closed on this topic.