views:

49

answers:

1

I have the following scenario:

public class FirstChildService : IChildService { }
public class SecondChildService : IChildService { }

public class MyService : IService {
    public MyService(IEnumerable<IChildService> childServices){ ... }
}

I'm currently registering all the child services and explicitly depending on them in the constructor of MyService, but what I'd like to do is have them all injected as part of a collection.

I can think of a few ways to do this:

  1. Using a facility
  2. Using a component property
  3. Registering the collection as a service

But all of them feel a bit... icky. What's the best way to manage this? Also, ideally I'd like to do this using the fluent API and constructor injection. I know it's possible to do something similar using properties:

http://www.castleproject.org/container/documentation/trunk/usersguide/arrayslistsanddicts.html

+1  A: 

So, recently I built a lightweight framework for running load tests. Similar to your scenario,

// each major component implements this interface, exposes
// explicit start, stop, pause, resume functionality
public interface IService { }

// first major component
public class ServiceA : IService { }

// second major component, may be run completely independently
// of ServiceA
public class ServiceB : IService { }

// if we want to run both ServiceA and ServiceB, we either
// have our application pull back two IService implementations
// by "id" [which kinda foils Inversion of Control], or we 
// create *another* service that simply acts against multiple 
// instances of IService. Application implements IService 
// itself, and in turn invokes start, stop, pause, and resume 
// on all child services
public class Application : IService 
{
    // we leave sub-service specification to container
    // configuration
    public Application (IEnumerable<IService> services) { }
}

// windows service execution context, this class basically
// instantiates a container [Castle Windsor], and pulls back
// an implementation of an IService, could be ServiceA, 
// ServiceB, or both a la Application
public class WindowsService : ServiceBase { }

So the trick is in two parts

  1. the "default" IService component must be Application, and
  2. Application must be given all instances of IService we need to run

I pretty much drive all my Inversion of Control [IoC] projects by config, but I presume there is an equivalent means of doing so via Castle Windsor's fluent api,

<castle>
  <components>
    <!-- 1. first trick is easy, make sure Application is declared first -->
    <component 
        id="Application" 
        service="SomeApp.Services.IService, SomeApp.Interfaces"
        type="SomeApp.Services.Application, SomeApp">
      <!-- 2. second trick is also easy ... -->
      <parameters>
        <!-- just specify the ctor parameter ... -->
        <services>
          <!-- followed by an array of named components! -->
          <array>
            <item>${ServiceA}</item>
            <item>${ServiceB}</item>
          </array>
        </services>
      </parameters>
    </component>

    <!-- 
        these definitions still register, but are not considered when
        a "blind" request is made for IService. Castle Windsor implements
        First Come First Serve [FCFS] ordering on registered components
    -->
    <component 
        id="ServiceA" 
        service="SomeApp.Services.IService, SomeApp.Interfaces"
        type="SomeApp.Services.ServiceA, SomeApp">
    </component>
    <component 
        id="ServiceB" 
        service="SomeApp.Services.IService, SomeApp.Interfaces"
        type="SomeApp.Services.ServiceB, SomeApp">
    </component>
  </components>
</castle>

For s***s and giggles, looked up the fluent api, equivalent.

// similar to app config approach
_container.
    Register (

    // 1. register Application first,
    Component.
    For<IService> ().
    ImplementedBy<Application> ().

    // 2. now this was interesting, to obtain components
    // registered later on, use late-binding lambda expressions!
    DynamicParameters
    (
        // k is a reference to kernel, d is parameters dictionary
        (k, d) =>
        {
            // "services" is name of ctor parameter, and we
            // assign this parameter an array of service 
            // references that are resolved on demand
            d["services"] = new IService[] 
            { 
                k.Resolve<IService> ("ServiceA"), 
                k.Resolve<IService> ("ServiceB"),
            };
        }
    ),

    // register other IService implementations :)
    Component.For<IService> ().ImplementedBy<ServiceA> ().Named ("ServcieA"),
    Component.For<IService> ().ImplementedBy<ServiceB> ().Named ("ServcieB"));

le fin.

johnny g