2015-04-15 4 views
1

Я создаю приложение, в котором SignalR используется для трансляции твитов в реальном времени на карту. Я использую библиотеку C# Tweetinvi (tweetinvi.codeplex.com) для обработки всей логики, связанной с подключением к API потокового трафика Twitter.Инъекция Singleton Экземпляр класса в концентратор SignalR с использованием Autofac

API Twitter указывает, что только одно потоковое соединение может быть открыто для Twitter в любое время. Поскольку я использую SignalR, существует зависимость между потоковым соединением и классом Hub. Я знаю, что класс Hub является временным, что означает, что он создается каждый раз, когда клиент запрашивает его, поэтому мне нужно убедиться, что экземпляр класса Stream Stream, введенного в класс Hub, является одноэлементным, или по крайней мере 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 открыт метод лямбда, как показано ниже, который позволяет получать твиты в режиме реального времени, что я хотел бы быть в состоянии получить доступ из моей SignalR класс Hub:

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

источник для этого метода можно найти here

Я пытался реализовать Autofac, и кажется, что подключение к API Twitter происходит, однако больше ничего не происходит. Я попытался отладить это, но я не уверен, как отлаживать такой сценарий, используя инъекцию зависимостей. Мой класс 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(); 
    } 
} 

И, наконец, мой класс Owin Startup, где зарегистрировать свою зависимость и хаб с Autofac:

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

Извините, если этот вопрос немного беспорядка , Мне трудно понять, какую архитектуру мне нужно реализовать, чтобы это работало! Откройте для совета/рекомендации о том, как это можно улучшить или как это сделать!

+0

нет? builder.RegisterType () .SingleInstance(); – Ewan

+0

@Ewan Да, это кажется более подходящим - регистрация самого типа как отдельного экземпляра, а не создание нового экземпляра – adaam

ответ

1

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

  • вы подписавшись это событие потенциально несколько раз для одного клиента.Я не знаю API Twitter, который вы используете, но на этом обратите внимание на то, что вы называете i.StartStreamMatchingAllConditions() все эти времена, кажется мне неправильным
  • каждый раз, когда вы создаете закрытие над Clients члена что например, в вашем случае обработчик, который должен уйти, когда ступица разрушается (так, вероятно, вы утечка памяти)

что вам нужно сделать, учитывая, что ваши вызываете над Client.All, и, следовательно, это чистая трансляция не зависит от любым конкретным абонентом, является:

  • инициализировать соединение с Twitter в конструкторе TwitterStream службы
  • в том же месте (возможно, с какой-то косвенностью, но, вероятно, не нужно) взять экземпляр контекста хаба вашей 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 должен существовать, но в случае, если вам просто нужно сделать этот вид широковещания до , все, без специального кода, необходимого для контроля соединений или обработки вызовов, созданных клиентом, вполне может быть пустым, и все в порядке, что ваш фактический связанный с хабом код живет за его пределами и использует IHubContext для широковещательных сообщений. Такой код будет заботиться о том, чтобы обрабатывать все существующие подключенные клиенты каждый раз, когда появляется твит, поэтому нет необходимости отслеживать их.

Конечно, если у вас есть больше требований для фактического обращения с клиентами отдельно, тогда, возможно, все должно быть по-другому, но ваш код не заставит меня думать иначе.

+0

Я действительно создал это приложение раньше, и ранее я использовал 'GetHubContext' для получения ссылки на концентратор вне Сам класс-концентратор и моя потоковая передача оттуда (что почти так же, как вы описываете) - https://github.com/adaam2/TwitterTrendingMap/blob/master/TwitterLogic/TwitterStream.cs). Однако я хотел реорганизовать архитектуру приложения для использования инъекции зависимостей, чтобы я мог внедрить услугу TwitterStream в сам концентратор и использовать инверсию управления. Но вы правы, я в настоящее время подписываюсь на событие несколько раз, используя текущий код. – adaam

+0

Нет смысла делать это так, потому что вы вводите вещи не в то место. Концентратор не нуждается в этом сервисе, потому что вы только подталкиваете Tweets к клиентам, поэтому он не знает, что с ним делать. Фактически, как я уже упоминал, ваш концентратор может быть пустым, и вам не понадобится иначе. Логические контексты 2 (службы и концентратора) не соответствуют IMO. – Wasp

+0

Нужно отметить. Спасибо за вашу помощь – adaam