Как настроить Castle Windsor для динамического выбора провайдера на основе аргументов (кроме «name»), предоставленных Resolve () - PullRequest
3 голосов
/ 04 мая 2011

Я пытаюсь узнать, как использовать Castle Windsor IoC, и у меня возникают трудности с пониманием того, как настроить некоторые объекты, которые мне нужно разрешать динамически.По сути, у меня есть несколько реализаций IDataSource, и мне нужно выбрать реализацию для использования в зависимости от того, как был настроен конкретный «источник данных».Так что я мог бы иметь довольно много источников данных, настроенных для использования одной из 3 реализаций.Я ожидаю, что зависимый код получит зависимость от фабричного метода, который даст им правильный IDataSource, когда ему предоставляется «идентификатор источника данных» вместе с зависимостями, которые требуются реализациями источника данных (IPrincipal).

Я борюсь с тем, как правильно написать делегата регистрации для Виндзора.Ниже примерно то, что у меня есть.Я пытаюсь использовать метод DynamicParameters (который может быть неправильным для использования), чтобы выполнить логику, которая выясняет, какую реализацию использовать, и затем вызывать Resolve, чтобы извлечь эту конкретную версию.Но я не знаю, как вернуть разрешенный объект, так как DynamicParameters ожидает ComponentReleasingDelegate, что, как я предполагаю, означает, что это должно быть что-то вроде return k => { k.ReleaseComponent(dataSource); }.Но тогда как мне вернуть источник данных, полученный мной, обратно в контейнер, чтобы он мог вернуться к вызывающей стороне?

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(), мне бы очень хотелось, чтобы он пропустил фабричный метод, но я не знаю, как заставить его это делать.

1 Ответ

1 голос
/ 26 ноября 2011

Почему бы просто не создать собственный селектор типа компонента и решить, какой компонент загрузить на основе этого?

Типизированный завод - фабрики на основе интерфейса

...