How to configure Castle Windsor to dynamically select a provider based on arguments (other than the name) provided in Resolve ()

I'm trying to learn how to use Castle Windsor IoC, and I'm having some difficulty understanding how to configure some of the objects that I need for a dynamic solution. Basically, I have several IDataSource implementations, and I need to choose an implementation to use based on how a particular "data source" is configured. Therefore, I could have quite a few data sources configured to use one of three implementations. My expectation is that the dependent code will depend on the factory method, which will provide them with the correct IDataSource when it is provided with a “data source identifier” along with the dependencies required by the IPrincipal implementation.

I am struggling with the correct way to write a registration delegate for Windsor. Below is about what I have. I am trying to use a method DynamicParameters(which may turn out to be wrong to use) to execute a logic that determines which implementation to use and then calls Resolveto pull this particular version. But I don’t know how to return the allowed object, since it DynamicParametersexpects ComponentReleasingDelegate, which, as I suppose, means that it should be something like return k => { k.ReleaseComponent(dataSource); }. But then how can I provide the data source that I received back to the container so that it returns to the caller?

struct DataSourceInfo {
  string Id;
  string ProviderType;
}

interface ICatalog : IDictionary<string /* Id */, DataSourceInfo> {
  /* ... */
}

class Catalog : ICatalog {
  /* implement dictionary which looks up DataSourceInfo from their string id */
}

interface IDataSource { /* ... */ }

class Source1 : IDataSource {
  Source1(string id, IPrincipal principal) { /* ... */ }
}

class Source2 : IDataSource {
  Source2(string id, IPrincipal principal) { /* ... */ }
}

/* ... */
/* ... inside Windsor configuration section */
container.Register(Component.For<ICatalog>().LifeStyle.Singleton.ImplementedBy<Catalog>());

// Default service provider is a factory method which uses the string (data source id)
// and looks up the DataSourceInfo from the ICatalog.  It then uses info.ProviderType
// to request IoC to resolve that specific implementation and passes in "id" and "principal"
// to be used to resolve the dependencies of the implementation
container.Register(Component.For<IDataSource>().LifeStyle.Transient
  .DynamicParameters((kernel, context, args) => {
      if (args == null || !args.Contains("id") || !(args["id"] is string)) throw ApplicationException("bad args");

      var id = (string)args["id"];
      var catalog = kernel.Resolve<ICatalog>();
      DataSourceInfo info;
      try { info = catalog[id]; } finally { kernel.ReleaseComponent(catalog); }

      // Now resolve the actual IDataSource
      var dataSource = kernel.Resolve<IDataSource>(info.ProviderType, args);

      // How do I return dataSource???
    });

// Now register the actual implementations
container.Register(Component.For<IDataSource>().LifeStyle.Transient.ImplementedBy<Source1>().Named("Source1"));
container.Register(Component.For<IDataSource>().LifeStyle.Transient.ImplementedBy<Source2>().Named("Source2"));

/* ... */
/* some application startup code which configures some data sources */
class AppConfigurer {
  AppConfigurer(ICatalog catalog) {
    catalog["sourceA"] = new DataSourceInfo() { Id = "sourceA", ProviderType = "Source1" }; // data sourceA is provided by Source1 class
    catalog["sourceB"] = new DataSourceInfo() { Id = "sourceB", ProviderType = "Source2" }; // data sourceB is provided by Source2 class
    catalog["sourceC"] = new DataSourceInfo() { Id = "sourceC", ProviderType = "Source2" }; // data sourceC is provided by Source2 class
    catalog["sourceD"] = new DataSourceInfo() { Id = "sourceD", ProviderType = "Source2" }; // data sourceD is provided by Source2 class
    catalog["sourceE"] = new DataSourceInfo() { Id = "sourceE", ProviderType = "Source1" }; // data sourceE is provided by Source1 class        
  }
}

// Here is where I actually want to use IDataSources, and I do not want to know all the business about IDataSourceInfo.  I just know a dataSourceId and an IPrincipal and want to get an IDataSource to work with.
class Dependant {
  Dependant (Func<string, IPrincipal, IDataSource> factory) {
    var sourceA = factory("sourceA", somePrincipal); // sourceA.GetType() == typeof(Source1)
    var sourceB = factory("sourceB", somePrincipal); // sourceB.GetType() == typeof(Source2)
    var sourceC = factory("sourceC", somePrincipal); // sourceC.GetType() == typeof(Source2)
  }
}

: DynamicParameters UsingFactoryMethod , . , , , container.ResolveAll(), factory, , .

+3
1
+1

All Articles