0

SignalR не может иметь клиентские методы, которые возвращают значение. Поэтому я пытаюсь создать вспомогательный класс, чтобы сделать это возможным.синхронно вызывать метод клиентской стороны с SignalR

Так это то, что я пытаюсь сделать: со стороны

  1. сервера: Вызов метода клиента и предоставлять уникальный идентификатор запроса Client(clientId).GetValue(requestId)
  2. стороне сервера: Сохранить RequestID и ждать ответа с помощью ManualResetEvent
  3. Client сторона: Внутри void GetValue(Guid requestId) метод сервера вызовов hubProxy.Invoke("GetValueFinished", requestId, 10)
  4. Сторона сервера: найти метод ожидания по запросуId => установить возвращаемое значение => установить сигнал
  5. Сторона сервера: метод не дожидается до ManualResetEvent и возвращает полученное значение.

Я могу получить его работу к сожалению. Вот мой код:

public static class MethodHandler 
{ 
    private static ConcurrentDictionary<Guid, ReturnWaiter> runningMethodWaiters = new ConcurrentDictionary<Guid,ReturnWaiter>(); 

    public static TResult GetValue<TResult>(Action<Guid> requestValue) 
    { 
     Guid key = Guid.NewGuid(); 
     ReturnWaiter returnWaiter = new ReturnWaiter(key); 
     runningMethodWaiters.TryAdd(key, returnWaiter); 
     requestValue.Invoke(key); 
     returnWaiter.Signal.WaitOne(); 
     return (TResult)returnWaiter.Value; 
    } 

    public static void GetValueResult(Guid key, object value) 
    { 
     ReturnWaiter waiter; 
     if (runningMethodWaiters.TryRemove(key, out waiter)) 
     { 
      waiter.Value = value; 
     } 
    } 
} 

internal class ReturnWaiter 
{ 
    private ManualResetEvent _signal = new ManualResetEvent(false); 
    public ManualResetEvent Signal { get { return _signal; } } 
    public Guid Key {get; private set;} 

    public ReturnWaiter(Guid key) 
    { 
     Key = key; 
    } 

    private object _value; 
    public object Value 
    { 
     get { return _value; } 
     set 
     { 
      _value = value; 
      Signal.Set(); 
     } 
    } 
} 

Используя этот MethodHandler класс мне нужно иметь на стороне сервера два метода:

public int GetValue(string clientId) 
{ 
    return MethodHandler.GetValue<int>(key => Clients(clientId).Client.GetValue(key)); 
} 

public void GetValueResult(Guid key, object value) 
{ 
    MethodHandler.GetValueResult(key, value); 
} 

реализации на стороне клиента, как это:

// Method registration 
_hubProxy.On("GetValue", new Action<Guid>(GetValue)); 

public void GetValue(Guid requestId) 
{ 
    int result = 10; 
    _hubConnection.Invoke("GetValueResult", requestId, result); 
} 

ПРОБЛЕМА:
если вызываю сервер сторона GetValue("clientid"). Клиентский метод не будет вызываться. Если я прокомментирую returnWaiter.Signal.WaitOne();, вызывается клиентская сторона GetValue и вызывается серверная сторона GetValueResult. Но, конечно, на этот раз метод уже вернулся.

Я думал, что имеет отношение к ManualResetEvent, но даже с использованием while(!returnWaiter.HasValue) Thread.Sleep(100); эта проблема не будет устранена.

Любые идеи по устранению этой проблемы?

Заранее благодарен!

ответ

0

Проблема решена:

Проблема возникла только в Hub.OnConnected и Hub.OnDisconnected. У меня нет точного объяснения, почему, но, вероятно, эти методы должны быть в состоянии закончить, прежде чем он обработает ваш вызов метода клиенту.

Поэтому я изменил код:

public override Task OnConnected() 
{ 
    // NOT WORKING 
    Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key))); 

    // WORKING 
    new Thread(() => Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)))).Start(); 

    return base.OnConnected(); 
} 
2

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

Вы не показываете свой метод MethodHandler::Retrieve, но я могу догадаться, что это похоже, и это даже не настоящая проблема. Я должен сказать вам самым лучшим образом, что это действительно Плохая идея. Он просто не будет масштабироваться. Это будет работать только с одним экземпляром сервера SignalR, потому что вы полагаетесь на конкретные ресурсы машины (например, объекты ядра за ManualResetEvent), чтобы обеспечить блокировку. Возможно, вам не нужно масштабироваться за пределами одного сервера, чтобы соответствовать вашим требованиям, но это все еще ужасная трата ресурсов даже на одном сервере.

Вы на самом деле находитесь на правильном пути, когда клиент обращается с requestId в качестве корреляционного идентификатора. Почему вы не можете использовать эту корреляцию для возобновления логического выполнения любого процесса, который вы находитесь в середине на стороне сервера?Таким образом, ресурсы не хранятся во время ожидания отправки сообщения клиенту, а затем последующего сообщения GetValueResult в вашем примере, чтобы отправить экземпляр сервера.

+0

Благодаря объяснить вашу озабоченность. Мне не нужно масштабировать до нескольких серверов, но все же производительность должна быть очень хорошей. – hwcverwe

+0

Я изменил метод MethodHandler :: Retrieve' на 'MethodHandler :: GetValue' ;-) – hwcverwe

Смежные вопросы