2010-02-01 3 views
2

У нас есть ряд компонентов виндзора замка, объявленных в файле конфигурации. Некоторым компонентам, находящимся глубоко внутри, может потребоваться обслуживание других компонентов.Элементы замка располагают заказ

Проблема заключается в том, когда приложение закрывается и контейнер находится в распоряжении. Во время Dispose()/Stop() компонента Startable/Disposable (A), когда он требует услуг другого компонента (B) ComponentNotFoundException, затем поднимается. К этому времени B уже удален из контейнера.

Я заметил, что порядок объявлений компонентов в файле конфигурации приложения важен. И перестановка A и B решает проблему.

Есть ли лучший способ повлиять на порядок расположения компонентов?

Отредактировано: После запроса в комментариях я приведу здесь пример кода, который отбросит ComponentNotFoundException:

class Program 
{ 
    static void Main() 
    { 
     IoC.Resolve<ICriticalService>().DoStuff(); 
     IoC.Resolve<IEmailService>().SendEmail("Blah"); 
     IoC.Clear(); 
    } 
} 

internal class CriticalService : ICriticalService, IStartable 
{ 
    public void Start() 
    {} 

    public void Stop() 
    { 
     // Should throw ComponentNotFoundException, as EmailService is already disposed and removed from the container 
     IoC.Resolve<IEmailService>().SendEmail("Stopping"); 
    } 

    public void DoStuff() 
    {} 
} 

internal class EmailService : IEmailService 
{ 
    public void SendEmail(string message) 
    { 
     Console.WriteLine(message); 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("EmailService Disposed."); 
     GC.SuppressFinalize(this); 
    } 
} 

internal interface ICriticalService 
{ 
    void DoStuff(); 
} 

internal interface IEmailService : IDisposable 
{ 
    void SendEmail(string message); 
} 

public static class IoC 
{ 
    private static readonly IWindsorContainer _container = new WindsorContainer(new XmlInterpreter()); 

    static IoC() 
    { 
     _container.AddFacility<StartableFacility>(); 
     // Swapping the following 2 lines resolves the problem 
     _container.AddComponent<ICriticalService, CriticalService>(); 
     _container.AddComponent<IEmailService, EmailService>(); 
    } 

    public static void Clear() 
    { 
     _container.Dispose(); 
    } 

    public static T Resolve<T>() 
    { 
     return (T)_container[typeof(T)]; 
    } 
} 

Примечание: см комментарий в коде, как подкачка порядок вставки компонентов в контейнере решает проблему.

+0

Пуск/остановка - две проблемы, обеспечиваемые StartableFacility, и одноразовая - это еще одна проблема, не имеющая принципиального отношения к запуску/остановке. StartableFacility гарантирует, что проблема Stop является первой, которая выполняется при «снятии с эксплуатации» компонента. Если вы говорите, что Dispose() выполняется до Stop(), то это выглядит как ошибка. Можете ли вы показать нам какой-то код, может быть, выставить тестовый файл? –

+1

Спасибо за ваш комментарий. Мне кажется, что когда контейнер размещен, он выполняет итерацию через каждый запускаемый компонент и вызывает каждый Stop(), а затем Dispose() и удаляет его из контейнера. Хотя я ожидаю, что сначала он должен вызвать Stop() для всех запусков, а затем удалить их и удалить из контейнера. –

+1

Да, контейнер выполняет итерацию через каждый запускаемый компонент и вызывает каждый Stop(), а затем Dispose(), это поведение. Можете ли вы разместить тестовый файл, который выдает ComponentNotFoundException? –

ответ

5

Имея статический класс IoC, вы фактически используете контейнер в качестве локатора сервисов, тем самым теряя большую часть преимуществ инъекции зависимостей.

Проблема заключается в том, что без надлежащей инъекции Windsor не знает о зависимости CriticalService - IEmailService, поэтому он не может обеспечить надлежащий порядок удаления.

Если вы реорганизовать, чтобы сделать эту зависимость явно, Виндзор располагает компоненты в правильном порядке:

internal class CriticalService : ICriticalService, IStartable 
{ 
    private readonly IEmailService email; 

    public CriticalService(IEmailService email) { 
     this.email = email; 
    } 
... 
} 

Here's how it would look как после рефакторинга.

+0

Спасибо за помощь. Я полностью согласен с вашими комментариями о зависимости конструктора. Однако имейте в виду, что это всего лишь образец, наша система намного больше и сложнее. Этого нелегко добиться, но мы постараемся реорганизовать эту область. Что касается StartableFacility, я просто забыл добавить его к образцу кода (оставил его в файле конфигурации). См. Обновленный образец кода. Он не работает с возможностью запуска. –

+0

Протестировано с 2.1.1 и StartableFacility - ComponentNotFoundException –

+0

Кстати, каково ваше мнение об этом шаблоне Айенде: http://ayende.com/Blog/archive/2006/07/30/IoCAndFluentInterfaces.aspx –

2

Я лично чувствую, что любая система, требующая Dispose(), должна быть вызвана в определенном порядке, имеет недостаток в дизайне.

Dispose() должно всегда безопасно звонить. Ошибки должны возникать только в том случае, если компонент используется после удаления, а затем ObjectDisposedException имеет наибольший смысл. В таком случае я бы переработал ваши компоненты, чтобы они не использовали другую компоненту во время своего метода Dispose() (это действительно должно быть об очистке собственных, собственных ресурсов каждого компонента). Это полностью устранит эту проблему.

+2

С одной стороны, вы правы, и я должен, вероятно, перебирать все компоненты Startable и вызывать Stop() на них в том порядке, который мне нравится, а затем вызвать Castle.Windsor.IWindsorContainer.Dispose(). С другой стороны, я ожидал бы, что рамки Castle сделают это от моего имени, поскольку они предоставляют метод Dispose(). –

+0

«Одна из уникальных особенностей Windsor заключается в том, что он управляет жизненным циклом объектов, которые он создает для вас. Это означает, среди прочего, что он будет уничтожать все одноразовые объекты, которые он создает». –

+1

Это действительно не имеет никакого отношения к этому. Они будут вызывать Dispose на ваших компонентах, но ваши компоненты не должны полагаться на другие компоненты, чтобы существовать, как только Dispose() был вызван. Порядок, в котором происходит удаление, не имеет значения ... –

Смежные вопросы