Как разрешить сбор с параметром фильтрации?
Может ли Castle Windsor разрешить коллекцию, отфильтрованную по строковому параметру?
interface IViewFactory
{
IView[] GetAllViewsInRegion(string regionName);
}
Мое приложение определяет регионы как группы производных от IView типов. Когда я отображаю определенный регион во время выполнения, я хочу разрешить экземпляр каждого типа IView в нем (а-ля Prism).
Я пытался сделать это с помощью Типизированной фабрики Замка, Участников конструирования ComponentModel и Селекторов обработчиков, но я не могу понять, как отобразить несколько типов в строку так, чтобы Замок мог получить к ней доступ, или как расширить Замок до проверьте строку, когда она решает, какие типы попытаться разрешить и вернуть в контейнер.
1 ответ
Строго ли необходим выбор по строке? Возможно ли вместо этого, чтобы все реализации IView в одном и том же "регионе" реализовали специальный интерфейс, производный от IView? Затем вы можете использовать WindsorContainer.ResolveAll() (передавая свой специфичный для региона IView как T), чтобы разрешить реализации для рассматриваемого региона (или вы можете использовать один из резольверов Collection для выполнения инжекции конструктора).
В общем, когда я пытаюсь делать подобные вещи с Виндзором, я прилагаю все усилия, чтобы использовать систему типов (и поддержку Виндзора), прежде чем прибегать к строковым решениям.
Обновление: так как мы подтвердили, что в этом случае необходим выбор по строке, лучшее решение, которое я вижу, это просто проверить список обработчиков в ядре, которые удовлетворяют сервису IView, а затем отфильтровать по реализаторам, где находится регион (определенный через атрибут) соответствует тому, что мы хотим, затем разрешите эти реализации. Это кажется немного хакерским, но если у вас все в порядке с прямой ссылкой на контейнер в вашей реализации IViewFactory, это работает нормально. Ниже приведен тестовый пример, демонстрирующий решение.
[Test]
public void Test()
{
using (var factory = new ViewFactory())
{
var regionOneViews = factory.GetAllViewsInRegion("One");
Assert.That(regionOneViews, Is.Not.Null);
Assert.That(regionOneViews, Has.Length.EqualTo(2));
Assert.That(regionOneViews, Has.Some.TypeOf<RegionOneA>());
Assert.That(regionOneViews, Has.Some.TypeOf<RegionOneB>());
var regionTwoViews = factory.GetAllViewsInRegion("Two");
Assert.That(regionTwoViews, Is.Not.Null);
Assert.That(regionTwoViews, Has.Length.EqualTo(1));
Assert.That(regionTwoViews, Has.Some.TypeOf<RegionTwoA>());
}
}
}
public interface IViewFactory
{
IView[] GetAllViewsInRegion(string regionName);
}
public class ViewFactory : IViewFactory, IDisposable
{
private readonly WindsorContainer _container;
public ViewFactory()
{
_container = new WindsorContainer();
_container.Register(
Component.For<IView>().ImplementedBy<RegionOneA>(),
Component.For<IView>().ImplementedBy<RegionOneB>(),
Component.For<IView>().ImplementedBy<RegionTwoA>()
);
}
public IView[] GetAllViewsInRegion(string regionName)
{
return _container.Kernel.GetHandlers(typeof (IView))
.Where(h => IsInRegion(h.ComponentModel.Implementation, regionName))
.Select(h => _container.Kernel.Resolve(h.ComponentModel.Name, typeof (IView)) as IView)
.ToArray();
}
private bool IsInRegion(Type implementation,
string regionName)
{
var attr =
implementation.GetCustomAttributes(typeof (RegionAttribute), false).SingleOrDefault() as RegionAttribute;
return attr != null && attr.Name == regionName;
}
public void Dispose()
{
_container.Dispose();
}
}
public interface IView {}
[Region("One")]
public class RegionOneA : IView {}
[Region("One")]
public class RegionOneB : IView {}
[Region("Two")]
public class RegionTwoA : IView {}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class RegionAttribute : Attribute
{
private readonly string _name;
public RegionAttribute(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
}
}