使用 Autofac 将 class 的单例实例注入 SignalR Hub
Injecting Singleton Instance of class into SignalR Hub using Autofac
我正在创建一个应用程序,其中 SignalR 用于将实时推文广播到地图。我正在使用 C# Tweetinvi 库 (tweetinvi.codeplex.com) 来处理与连接到 Twitter 流相关的所有逻辑 API。
推特API规定任何时候只能对推特开放一个流媒体连接。当我使用 SignalR 时,Streaming 连接和 Hub class 之间存在依赖关系。我知道 Hub class 是瞬态的,这意味着每次客户端请求它时都会创建它,所以我需要确保我的 Twitter Stream class 实例注入到 Hub class 是单例,或者至少 IFilteredStream
在应用程序的生命周期中只创建一次。这是连接到 API:
的样板代码
public class TweetStream
{
private IFilteredStream _stream;
public TweetStream()
{
var consumerKey = ConfigurationManager.AppSettings.Get("twitter:ConsumerKey");
var consumerSecret = ConfigurationManager.AppSettings.Get("twitter:ConsumerSecret");
var accessKey = ConfigurationManager.AppSettings.Get("twitter:AccessKey");
var accessToken = ConfigurationManager.AppSettings.Get("twitter:AccessToken");
TwitterCredentials.SetCredentials(accessKey, accessToken, consumerKey, consumerSecret);
_stream = Stream.CreateFilteredStream();
}
// Return singular instance of _stream to Hub class for usage.
public IFilteredStream Instance
{
get { return _stream; }
}
}
IFilteredStream 接口公开了如下所示的 lambda 方法,它允许实时接收推文,我希望能够从我的 SignalR Hub class:
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
可以找到此方法的来源here
我已尝试实施 Autofac,似乎与 Twitter API 的连接发生了,但没有发生更多事情。我试过对此进行调试,但不确定如何使用依赖注入来调试这种情况。我的 Hub class 目前看起来像这样:
public class TwitterHub : Hub
{
private readonly ILifetimeScope _scope;
private readonly TweetStream _stream;
// Inject lifetime scope and resolve reference to TweetStream
public TwitterHub(ILifetimeScope scope)
{
_scope = scope.BeginLifetimeScope();
_stream = scope.Resolve<TweetStream>();
var i = _stream.Instance;
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
i.StartStreamMatchingAllConditions();
}
}
最后,我的 OWIN Startup class,我在其中使用 Autofac 注册了我的依赖项和 Hub:
[assembly: OwinStartup(typeof(TwitterMap2015.App_Start.OwinStartup))]
namespace TwitterMap2015.App_Start
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// use hubconfig, not globalhost
var hubConfig = new HubConfiguration {EnableDetailedErrors = true};
builder.RegisterHubs(Assembly.GetExecutingAssembly()); // register all SignalR hubs
builder.Register(i => new TweetStream()).SingleInstance(); // is this the correct way of injecting a singleton instance of TweetStream?
var container = builder.Build();
hubConfig.Resolver = new AutofacDependencyResolver(container);
app.MapSignalR("/signalr", hubConfig);
}
}
}
抱歉,如果这个问题有点乱,我很难理解我需要实现什么样的架构才能让它正常工作!接受关于如何改进或应该如何完成的建议/建议!
IMO 这行不通,因为您正在连接事件以调用特定集线器实例的上下文,而不管与 Autofac 相关的任何代码(这也可能有问题,但我不是这方面的专家) . 每次发生新连接或从客户端调用方法时都会调用集线器的构造函数,因此:
- 您可能会为每个客户多次订阅该事件。我不知道你用的是哪个 Twitter API,但在这张纸条上,你一直打电话给
i.StartStreamMatchingAllConditions()
对我来说似乎是错误的
- 每次你在事件处理程序中 that 实例的
Clients
成员上创建一个闭包时,它应该在 hub 被销毁时消失(所以可能你正在泄漏内存)
鉴于您正在呼叫 Client.All
,因此这是一个独立于任何特定呼叫者的纯广播,您需要做的是:
- 在
TwitterStream
服务的构造函数中初始化您的 Twitter 连接
- 在同一个地方(也许有一些间接的,但可能没有必要)获取你的
TwitterHub
的 hub context 的实例
- 订阅事件并使用您刚刚检索到的上下文对其进行广播
这样的构造函数可能如下所示:
public service TwitterStream : ??? <- an interface here?
{
...
public TwitterStream (ILifetimeScope scope ??? <- IMO you don't need this...)
{
//Autofac/Twitter stuff
...
var context = GlobalHost.DependencyResolver.GetHubContext<TwitterHub>();
_stream.MatchingTweetReceived += (sender, args) => {
context.Clients.All.broadcast(args.Tweet);
};
//maybe more Autofac/Twitter stuff
...
}
...
}
TwitterHub
必须存在,但如果您只需要它对 all 进行这种广播,则无需特殊代码来监视连接或处理客户端-生成的调用,它很可能是空的,你的实际与集线器相关的代码位于它之外并使用 IHubContext
来广播消息就好了。每次推文到达时,这样的代码将负责处理所有现有连接的客户端,因此无需跟踪它们。
当然,如果您对实际单独处理客户有更多要求,那么事情可能需要有所不同,但您的代码并没有让我有其他想法。
我正在创建一个应用程序,其中 SignalR 用于将实时推文广播到地图。我正在使用 C# Tweetinvi 库 (tweetinvi.codeplex.com) 来处理与连接到 Twitter 流相关的所有逻辑 API。
推特API规定任何时候只能对推特开放一个流媒体连接。当我使用 SignalR 时,Streaming 连接和 Hub class 之间存在依赖关系。我知道 Hub class 是瞬态的,这意味着每次客户端请求它时都会创建它,所以我需要确保我的 Twitter Stream class 实例注入到 Hub class 是单例,或者至少 IFilteredStream
在应用程序的生命周期中只创建一次。这是连接到 API:
public class TweetStream
{
private IFilteredStream _stream;
public TweetStream()
{
var consumerKey = ConfigurationManager.AppSettings.Get("twitter:ConsumerKey");
var consumerSecret = ConfigurationManager.AppSettings.Get("twitter:ConsumerSecret");
var accessKey = ConfigurationManager.AppSettings.Get("twitter:AccessKey");
var accessToken = ConfigurationManager.AppSettings.Get("twitter:AccessToken");
TwitterCredentials.SetCredentials(accessKey, accessToken, consumerKey, consumerSecret);
_stream = Stream.CreateFilteredStream();
}
// Return singular instance of _stream to Hub class for usage.
public IFilteredStream Instance
{
get { return _stream; }
}
}
IFilteredStream 接口公开了如下所示的 lambda 方法,它允许实时接收推文,我希望能够从我的 SignalR Hub class:
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
可以找到此方法的来源here
我已尝试实施 Autofac,似乎与 Twitter API 的连接发生了,但没有发生更多事情。我试过对此进行调试,但不确定如何使用依赖注入来调试这种情况。我的 Hub class 目前看起来像这样:
public class TwitterHub : Hub
{
private readonly ILifetimeScope _scope;
private readonly TweetStream _stream;
// Inject lifetime scope and resolve reference to TweetStream
public TwitterHub(ILifetimeScope scope)
{
_scope = scope.BeginLifetimeScope();
_stream = scope.Resolve<TweetStream>();
var i = _stream.Instance;
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
i.StartStreamMatchingAllConditions();
}
}
最后,我的 OWIN Startup class,我在其中使用 Autofac 注册了我的依赖项和 Hub:
[assembly: OwinStartup(typeof(TwitterMap2015.App_Start.OwinStartup))]
namespace TwitterMap2015.App_Start
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// use hubconfig, not globalhost
var hubConfig = new HubConfiguration {EnableDetailedErrors = true};
builder.RegisterHubs(Assembly.GetExecutingAssembly()); // register all SignalR hubs
builder.Register(i => new TweetStream()).SingleInstance(); // is this the correct way of injecting a singleton instance of TweetStream?
var container = builder.Build();
hubConfig.Resolver = new AutofacDependencyResolver(container);
app.MapSignalR("/signalr", hubConfig);
}
}
}
抱歉,如果这个问题有点乱,我很难理解我需要实现什么样的架构才能让它正常工作!接受关于如何改进或应该如何完成的建议/建议!
IMO 这行不通,因为您正在连接事件以调用特定集线器实例的上下文,而不管与 Autofac 相关的任何代码(这也可能有问题,但我不是这方面的专家) . 每次发生新连接或从客户端调用方法时都会调用集线器的构造函数,因此:
- 您可能会为每个客户多次订阅该事件。我不知道你用的是哪个 Twitter API,但在这张纸条上,你一直打电话给
i.StartStreamMatchingAllConditions()
对我来说似乎是错误的 - 每次你在事件处理程序中 that 实例的
Clients
成员上创建一个闭包时,它应该在 hub 被销毁时消失(所以可能你正在泄漏内存)
鉴于您正在呼叫 Client.All
,因此这是一个独立于任何特定呼叫者的纯广播,您需要做的是:
- 在
TwitterStream
服务的构造函数中初始化您的 Twitter 连接 - 在同一个地方(也许有一些间接的,但可能没有必要)获取你的
TwitterHub
的 hub context 的实例
- 订阅事件并使用您刚刚检索到的上下文对其进行广播
这样的构造函数可能如下所示:
public service TwitterStream : ??? <- an interface here?
{
...
public TwitterStream (ILifetimeScope scope ??? <- IMO you don't need this...)
{
//Autofac/Twitter stuff
...
var context = GlobalHost.DependencyResolver.GetHubContext<TwitterHub>();
_stream.MatchingTweetReceived += (sender, args) => {
context.Clients.All.broadcast(args.Tweet);
};
//maybe more Autofac/Twitter stuff
...
}
...
}
TwitterHub
必须存在,但如果您只需要它对 all 进行这种广播,则无需特殊代码来监视连接或处理客户端-生成的调用,它很可能是空的,你的实际与集线器相关的代码位于它之外并使用 IHubContext
来广播消息就好了。每次推文到达时,这样的代码将负责处理所有现有连接的客户端,因此无需跟踪它们。
当然,如果您对实际单独处理客户有更多要求,那么事情可能需要有所不同,但您的代码并没有让我有其他想法。