使用Autofac将Singleton实例类注入SignalR Hub

时间:2015-04-15 10:04:15

标签: c# asp.net-mvc signalr autofac tweetinvi

我正在创建一个应用程序,其中SignalR用于向地图广播实时推文。我正在使用C#Tweetinvi库(tweetinvi.codeplex.com)来处理与连接到Twitter Streaming API相关的所有逻辑。

Twitter API指定任何时候只能向Twitter打开一个流连接。当我使用SignalR时,Streaming连接和Hub类之间存在依赖关系。我知道Hub类是瞬态的,这意味着每次客户端请求它时都会创建它,因此我需要确保注入Hub类的我的Twitter Stream类的实例是单例,或者至少{{1} }仅在应用程序的生命周期中创建一次。以下是连接到API的样板代码:

IFilteredStream

IFilteredStream接口公开了一个lambda方法,允许实时接收推文,我希望能够从我的SignalR Hub类中访问:

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; }
        }

    }

可以找到此方法的来源here

我试图实现Autofac,似乎与Twitter API的连接发生了,但是没有更多的事情发生。我试图调试这个,但不确定如何使用依赖注入调试这样的场景。我的Hub类目前看起来像这样:

_stream.MatchingTweetReceived += (sender, args) => {
        Clients.All.broadcast(args.Tweet);
};

最后,我的OWIN Startup类,我用Autofac注册我的依赖项和Hub:

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();
    }
}

很抱歉,如果这个问题有点乱,我很难理解我需要实现什么样的架构来实现这个功能!欢迎提出有关如何改进或如何改进的建议/建议!

1 个答案:

答案 0 :(得分:1)

IMO这不起作用,因为您将事件连接到特定集线器实例的上下文,无论与Autofac相关的任何代码(可能还有问题,但我不是它的专家)。 每次发生新连接或从客户端调用方法时,都会调用hub的构造函数,因此:

  • 您每个客户端可能会多次订阅该事件。我不知道您正在使用的Twitter API,但是在这个注意事项中,您所有这些时间都致电i.StartStreamMatchingAllConditions()这一事实对我来说似乎不对。
  • 每次在事件处理程序中的 实例的Clients成员上创建一个闭包,当集线器被销毁时它应该消失(所以你可能正在泄漏内存) )

你需要做什么,因为你正在呼叫Client.All,因此这是一个独立于任何特定呼叫者的纯广播,是:

  • TwitterStream服务
  • 的构造函数中初始化您的Twitter连接
  • 在同一个地方(可能有一些间接,但可能没有必要)获取TwitterHub
  • 集线器上下文的实例
  • 订阅该活动并使用您刚刚检索到的上下文通过它进行广播

这样的构造函数可能如下所示:

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来广播消息。这样的代码将在每次推文到达时处理所有现有的连接客户端,因此无需跟踪它们。

当然,如果您对实际处理客户有更多要求,那么事情可能需要有所不同,但您的代码并没有让我想到其他。