Компоненты замка располагают порядком - PullRequest
2 голосов
/ 02 февраля 2010

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

Проблема в том, что приложение закрывается и контейнер удаляется. Во время 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)];
    }
}

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

Ответы [ 2 ]

5 голосов
/ 02 февраля 2010

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

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

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

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

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

Вот как это будет выглядеть после рефакторинга.

2 голосов
/ 02 февраля 2010

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

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

...