SignalR和Castle Windsor的正确Hub依赖生命周期管理

37

我有一些SignalR信令中心,可能需要访问一些短暂的和单例依赖项。挂钩Hub的创建很容易,也很有效,但是SignalR会在创建的Hub上执行自己的Dispose()调用,而不是通知依赖项解析器并让其参与处理。

如果依赖关系已注册为单例,则这并不是太大的问题,但是如果它们已注册为瞬态,则它们将永远不会被处理(如果需要),并且Windsor将保持它们处于活动状态,直到收集Windsor容器(当Web服务器关闭时)。

我看到几种可能的处理方式...

a)这里有人指出了一种子类化SignalR的HubDispatcher类的方法,以便可以进行适当的清理。 它不是SignalR标准DependencyResolver的一部分,因此可能很难/不可能实现

b)SignalR中的其他类,管道的其他地方,可以被覆盖或轻松替换,以便我们可以子类化HubDispatcher并确保使用该子类。 从我所了解的情况来看,这必须是Owin中间件类HubDispatcherMiddleware。 是否有某种方法可以强制Owin不注册此类,而是注册我的此类版本(反过来使用我的自己的HubDispatcher)?

c)有一种方式可以拦截SignalR对我的Hub类所做的Dispose()调用,以便可以向Windsor发出调用,确保任何依赖项都被正确处理并从容器中释放

d)刻意避免使用瞬态生命周期依赖项,而是传递类型工厂,以便我们可以通过Hub内的类型化工厂解析和释放每个依赖项

目前,我只知道如何执行(d)。 (a)或(b)将很棒。 (c)主要由这篇文章http://kozmic.net/2010/01/27/transparently-releasing-components-in-windsor/涵盖,但是拦截程序需要通过IDisposable调用Dispose()。 SignalR的HubDispather类对中心清理的实现为

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

不需要对IDisposable进行强制转换......另外,Hub类上的Dispose()是虚拟的,并且这篇博客暗示了虚拟Dispose()可能会增加一些复杂性(我不确定有多少复杂性,也不知道Castle的拦截器是否可以规避掉那个未转换为IDisposable的问题)。

我知道我的问题只适用于很小众的用户——那些使用Windsor和SignalR并关心除了解决依赖项之外的内容。我找到的每一个例子,包括StackOverflow上的那些,似乎都忽略了依赖项的释放。

谢谢!


2
当库声称支持IoC但只支持Resolve()而不支持Release()时,这是令人沮丧的。 - Phil Degenhardt
当然...我在考虑编译自己的SignalR副本,如果我能解决GitHub上的问题,就会根据需要的(非常轻微的)代码更改创建某种拉取请求。 - Ian Yates
1
对于其他人来说,截至2014年2月,官方建议在https://github.com/SignalR/SignalR/issues/2908上提供。未来的版本应该会有所改进。与此同时,我可能只会编译自己稍微调整过的副本。 - Ian Yates
我确定我已经理解了他们的回答,那就是“等到下一个版本发布”?如果是这样,那么作为临时措施,最好的方法是创建一个抽象的 Hub 子类:WindsorHub,并将处理逻辑放在其中,然后从那里继承。 - Stu
1
最终我在Windsor中使用单例生命周期注册了我的中心节点。注入到中心节点的要么是其他单例对象,要么是类型工厂,它们被调用以获取所需的对象,使用它,然后调用工厂的释放方法。 - Ian Yates
撤回我之前的建议 :) 我刚刚转而使用瞬态注册。我的SignalR Windsor依赖项解析器会这样处理:如果解析出实现IHub的内容,则在返回之前立即从容器中释放它。对我来说,这很有效,因为Hub的处理是无害的,并且至少对我来说,我只将长期存在的单例注入到我的Hub中,因此释放Hub实例不会对其依赖项产生任何影响。这样,我就可以避免在并行请求上混合使用Groups属性。 - Ian Yates
1个回答

2

我遇到了一个类似的问题,但是使用的是Unity而不是Castle Windsor。

我的要求:

  • 我想避免在容器中注册单例。
  • 所有对象都在Hub中解析,并应在Hub销毁时进行处理。
  • 在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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接