Вызов метода сервера, находящегося вне класса Hub, из клиента в SignalR

Рассмотрим класс ниже:

using Microsoft.AspNet.SignalR;
public class TwitterStream
    {
        // Hub Context
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<GeoFeedHub>();

        public void ChangeStreamBounds(double latitude, double longitude)
        {
            Debug.WriteLine(latitude + "-" + longitude);
        }

        // Lots of other interesting code redacted
   }

Можно ли назвать ChangeStreamBounds метод от клиента, даже если он находится вне класса Hub? Можно вызывать клиентские функции с сервера (и из-за пределов класса Hub), но возможно ли сделать это наоборот?

К сожалению, я загнал себя в угол, и код должен выполняться из написанного мною класса (а не из самого Hub - если, конечно, вы не можете запустить SignalR Hub как фабрику задач)

1 ответ

Решение

Там может быть ответ на ваш вопрос, который включает в себя HubConnectionс и IHubProxys (который позволяет связывать вызовы методов-концентраторов) или API более низкого уровня, но я думаю, что вы, возможно, ошибаетесь.

Концептуально, вы хотите GeoFeedHub обрабатывать запросы клиентов, а TwitterStream класс для обработки взаимодействия с API Twitter. Таким образом, ваш GeoFeedHub класс зависит от TwitterStream,

Это хорошо, что ваш TwitterStream класс имеет async методы, и это полностью поддерживается в SignalR. Вы можете иметь асинхронные методы Hub, которые вызывают TwitterStream, что устраняет необходимость вашего TaskFactory использование в Global.asax,

Вместо создания вашего TwitterStream при запуске приложения и попытке найти способ привязать к нему вызовы Hub (обратная зависимость и нарушение принципа единой ответственности), было бы лучше, если бы ваш Hub выступал в качестве точки соприкосновения между вашими клиентами в реальном времени и внедрял экземпляр TwitterStream в GeoFeedHub таким образом, концентратор может получить доступ к API Twitter.


Вот пример кода, который должен проиллюстрировать эту идею:

public class GeoFeedHub : Hub
{
    // Declare dependency on TwitterStream class
    private readonly TwitterStream _twitterStream;

    // Use constructor injection to get an instance of TwitterStream
    public GeoFeedHub(TwitterStream _twitterStream)
    {
        _twitterStream = _twitterStream;
    }

    // Clients can call this method, which uses the instance of TwitterStream
    public async Task SetStreamBounds(double latitude, double longitude)
    {
        await _twitterStream.SetStreamBoundsAsync(latitude, longitude);
    }
}


public class TwitterStream
{
    public TwitterStream() 
    {
    }

    public async Task SetStreamBoundsAsync(double latitude, double longitude)
    {
        // Do something with Twitter here maybe?
        await SomeComponent.SetStreamBoundsAsync(latitude, longitude);
    }

    // More awesome code here
}

Я использовал DI в этом примере, так что вот вам немного кода, который вам понадобится, чтобы подключить его. Этот код вошел бы в ваш App_Start папка:

// Configure Unity as our DI container
public class UnityConfig
{

    private static readonly Lazy<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();
        RegisterTypes(container);
        return container;
    });

    public static IUnityContainer GetConfiguredContainer()
    {
        return Container.Value;
    }


    private static void RegisterTypes(IUnityContainer container)
    {
        var twitterService = new TwitterService();
        container.RegisterInstance(twitterService);

        /*
         * Using RegisterInstance effectively makes a Singleton instance of 
         * the object accessible throughout the application.  If you don't need 
         * (or want) the class to be shared among all clients, then use 
         * container.RegisterType<TwitterService, TwitterService>();
         * which will create a new instance for each client (i.e. each time a Hub
         * is created, you'll get a brand new TwitterService object)
         */
    }
}


// If you're using ASP.NET, this can be used to set the DependencyResolver for SignalR 
// so it uses your configured container

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(UnitySignalRActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(UnitySignalRActivator), "Shutdown")]

public static class UnitySignalRActivator
{
    public static void Start() 
    {
        var container = UnityConfig.GetConfiguredContainer();
        GlobalHost.DependencyResolver = new SignalRUnityDependencyResolver(container);
    }

    public static void Shutdown()
    {
        var container = UnityConfig.GetConfiguredContainer();
        container.Dispose();
    }
}

public class SignalRUnityDependencyResolver : DefaultDependencyResolver
{
    private readonly IUnityContainer _container;

    public SignalRUnityDependencyResolver(IUnityContainer container)
    {
        _container = container;
    }

    public override object GetService(Type serviceType)
    {
        return _container.IsRegistered(serviceType) 
            ? _container.Resolve(serviceType) 
            : base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.IsRegistered(serviceType) 
            ? _container.ResolveAll(serviceType) 
            : base.GetServices(serviceType);
    }
}
Другие вопросы по тегам