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