Подтвердить что ты не робот

Правильное управление жизненным циклом зависимой сети для SignalR и Castle Windsor

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

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

Я вижу несколько возможных способов обращения с этим...

a) Кто-то здесь указывает на способ подкласса класса SignalR HubDispatcher, чтобы он мог выполнять правильную утилизацию. Это не часть стандарта SignalR DependencyResolver, поэтому это может быть сложно/невозможно

b) Некоторые другие классы в SignalR, в другом месте в конвейере, могут быть переопределены или легко заменены, чтобы мы могли подклассифицировать HubDispatcher и обеспечить использование подкласса. Из того, что я могу сказать, это должен быть класс промежуточного ПО Owin HubDispatcherMiddleware. Есть ли способ заставить Owin не регистрировать этот класс и вместо этого зарегистрировать мою собственную версию (которая, в свою очередь, использует мой собственный HubDispatcher)?

c) Там какой-то способ перехватить вызов Dispose(), сделанный SignalR на моих классах Hub, чтобы вызов мог быть возвращен в Виндзор, чтобы гарантировать, что любые зависимости правильно размещены и выпущены из контейнера

d) Старайтесь избегать использования переходных привычек образа жизни и вместо этого переходить на типизированные фабрики, чтобы мы могли разрешать и освобождать каждую зависимость с помощью типизированного factory внутри концентратора

В настоящий момент (d) я единственный, кто умеет делать. (a) или (b) было бы здорово. (c) в основном покрывается этой записью http://kozmic.net/2010/01/27/transparently-releasing-components-in-windsor/, однако для перехватчика требуется, чтобы Dispose() вызывался через IDisposable. Внедрение XubDispather класса XubDispather является

private static void DisposeHubs(IEnumerable<IHub> hubs)
{
    foreach (var hub in hubs)
    {
        hub.Dispose();
    }
}

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

Я ценю, что написал этот вопрос для довольно узкой аудитории - тех, кто использовал Windsor AND SignalR и заботится не только о разрешении зависимостей. Каждый пример, который я нашел, в том числе и на StackOverflow, просто игнорирует выпуск зависимостей.

Спасибо!

4b9b3361

Ответ 1

У меня была похожая проблема, но с Unity вместо Castle Windsor.

Мои требования:

  • Я хотел избежать регистрации в контейнере однократной регистрации.
  • Все объекты разрешены в концентраторе и должны располагаться при уничтожении хаба.
  • Регистрации повторно используются через Web Api и SignalR.
  • Время жизни объекта управляется HierarchicalLifetimeManager - дочерние контейнеры разрешают и управляют отдельными экземплярами объектов. Зарегистрировано вот так:
container.RegisterType<IMessageService, MessageService>(new HierarchicalLifetimeManager());

Это мое решение:

[HubName("exampleHub")]
public class ExampleHub : Hub
{
    IUnityContainer _container;

    public CarrierApiHub(IUnityContainer container) // container itself injected in hub
    {
        _container = container.CreateChildContainer(); // child container derived from the main container.
    }

    public async Task<int> UnreadMessagesCount()
    {
        // Here i'm resolving instance of IMessageService which depends on
        // other registrations specified on the container. Full object graph
        // is constructed and destroyed on hub disposal.
        var messageSvc = _container.Resolve<IMessageService>();
        return await messageSvc.CountUnreadOf(UserId);
    }

    protected override void Dispose(bool disposing)
    {
        _container.Dispose(); // child container destroyed. all resolved objects disposed.
        base.Dispose(disposing);
    }

    private int UserId
    {
        get
        {
            // only an example
            var claim = ((ClaimsPrincipal)Context.User).GetClaim("user_id");
            return int.Parse(claim.Value);
        }
    }
}

Конфигурация преобразователя SignalR и зависимостей:

public static class ConfigureSignalR
{
    public static void Initialize(UnityContainer unityContainer, IAppBuilder app)
    {
        app.Map("/signalr", map =>
        {
            var resolver = new AppSignalRDependencyResolver(unityContainer);

            map.UseCors(CorsOptions.AllowAll);

            var hubConfiguration = new HubConfiguration
            {
                EnableJavaScriptProxies = false,
                EnableJSONP = true, // Required for IE 9 (supports only polling)
                Resolver = resolver
            };

            map.RunSignalR(hubConfiguration);
        });
    }
}

Репликация зависимостей:

public class AppSignalRDependencyResolver : DefaultDependencyResolver
{
    protected IUnityContainer _container;

    public AppSignalRDependencyResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this._container = container.CreateChildContainer();
    }

    public override object GetService(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return base.GetService(serviceType);
        }
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return _container.ResolveAll(serviceType).Concat(base.GetServices(serviceType));
        }
        catch (ResolutionFailedException)
        {
            return base.GetServices(serviceType);
        }
    }

    protected override void Dispose(bool disposing)
    {
        _container.Dispose();
        base.Dispose(disposing);
    }
}