使用 SimpleInjector 获取 HubContext

Get the HubContext using SimpleInjector

This guide 似乎不适用于 SimpleInjector。

我的 OWIN 启动看起来像这样:

container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();

container.RegisterSingleton(() => new SimpleInjectorSignalRDependencyResolver(_container));
container.RegisterSingleton(() =>
    new HubConfiguration()
    {
        EnableDetailedErrors = true,
        Resolver = _container.GetInstance<SimpleInjectorSignalRDependencyResolver>()
    });

container.RegisterSingleton<IHubActivator, SimpleInjectorHubActivator>();
container.RegisterSingleton<IStockTicker,StockTicker>();
container.RegisterSingleton<HubContextAdapter<StockTickerHub, IStockTickerHubClient>>();
container.RegisterSingleton(() => GlobalHost.ConnectionManager);
container.Verify();

GlobalHost.DependencyResolver = container.GetInstance<SimpleInjectorSignalRDependencyResolver>();

app.Use(async (context, next) =>
{
    using (container.BeginExecutionContextScope())
    {
        await next();
    }
});

app.MapSignalR(container.GetInstance<HubConfiguration>());

HubContextAdapter 看起来像这样:

public class HubContextAdapter<THub, TClient>
    where THub : Hub
    where TClient : class
{
    private readonly IConnectionManager _manager;

    public HubContextAdapter(IConnectionManager manager)
    {
        _manager = manager;
    }

    public IHubContext<TClient> GetHubContext()
    {
        return _manager.GetHubContext<THub, TClient>();
    }
}

SimpleInjectorSignalRDependencyResolver 看起来像:

public class SimpleInjectorSignalRDependencyResolver : DefaultDependencyResolver
{
    public SimpleInjectorSignalRDependencyResolver(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public override object GetService(Type serviceType)
    {
        return _serviceProvider.GetService(serviceType) ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        var @this = (IEnumerable<object>)_serviceProvider.GetService(
            typeof(IEnumerable<>).MakeGenericType(serviceType));

        var @base = base.GetServices(serviceType);

        return @this == null ? @base : @base == null ? @this : @this.Concat(@base);
    }

    private readonly IServiceProvider _serviceProvider;
}

StockTicker 看起来像:

public class StockTicker : IStockTicker
{
    private readonly HubContextAdapter<StockTickerHub, IStockTickerHubClient> _context;

    public StockTicker(HubContextAdapter<StockTickerHub, IStockTickerHubClient> context)
    {
        _context = context;
    }
}

StockTicker 滴答并调用所有客户端更新时,客户端方法未被调用并且没有网络流量。

SimpleInjector 想要在验证后或第一次 GetInstance 调用后实例化单例。这对于 SignalR 和 StockTicker 来说还为时过早,它将在 SimpleInjectorSignalRDependencyResolver 成为解析器之前采用 GlobalHost.ConnectionManager 的实例。

我选择把对IConnectionManager的依赖改成Lazy<IConnectionManager>,把对IStockTicker的依赖改成Lazy<IStockTicker>,这样注册就变成了下面这样:

container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();

container.RegisterSingleton(() => new SimpleInjectorSignalRDependencyResolver(_container));
container.RegisterSingleton(() =>
    new HubConfiguration()
    {
        EnableDetailedErrors = true,
        Resolver = _container.GetInstance<SimpleInjectorSignalRDependencyResolver>()
    });

container.RegisterSingleton<IHubActivator, SimpleInjectorHubActivator>();
container.RegisterSingleton<IStockTicker,StockTicker>();
container.RegisterSingleton<Lazy<IStockTicker>>(() => new Lazy<IStockTicker>(() => container.GetInstace<IStockTicker>()) );
container.RegisterSingleton<HubContextAdapter<StockTickerHub, IStockTickerHubClient>>();
container.RegisterSingleton(() => new Lazy<IConnectionManager>(() => GlobalHost.ConnectionManager));
container.Verify();