Usando observável.Fromevent ao chamar um serviço WCF em Silverlight
-
21-09-2019 - |
Pergunta
Estou tentando usar a estrutura reativa .NET para simplificar algumas chamadas assíncronas para um serviço WCF usado por um aplicativo Silverlight 3 que estou escrevendo.
O problema é que estou tendo dificuldade em encontrar uma maneira de estruturar meu código de uma maneira que funcione. Parte do problema, sem dúvida, é entender quais mecanismos estão disponíveis em reativo e como usá -los para resolver meu problema.
Estou tentando reunir uma série de chamadas de servidor WCF - se fossem síncronas, eles se pareceriam algo assim:
switch( CurrentVisualState )
{
case GameVisualState.Welcome:
m_gameState = m_Server.StartGame();
if( m_GameState.Bankroll < Game.MinimumBet )
NotifyPlayer( ... ); // some UI code here ...
goto case GameVisualState.HandNotStarted;
case GameVisualState.HandNotStarted:
case GameVisualState.HandCompleted:
case GameVisualState.HandSurrendered:
UpdateUIMechanics();
ChangeVisualState( GameVisualState.HandPlaceBet );
break;
case GameVisualState.HandPlaceBet:
UpdateUIMechanics();
// request updated game state from game server...
m_GameState = m_Server.NextHand( m_GameState, CurrentBetAmount );
if( CertainConditionInGameState( m_GameState ) )
m_GameState = m_Server.CompleteHand( m_GameState );
break;
}
As chamadas para m_Server.XXXX()
Costumava ser implementado diretamente no aplicativo Silveright (portanto, eles poderiam ser síncronos) - mas agora são implementados em um serviço WCF. Desde que o Silverlight obriga você a ligar para os serviços da WCF de forma assíncrona - a reescrita deste bloco de código tem sido complicada.
Eu esperava usar Observable.FromEvent<>()
Para se inscrever nos vários XXXCompleted
Eventos que o código de proxy do WCF gera, mas não está claro para mim como fazer com que isso funcione. Minha tentativa original parecia algo como:
var startObs = Observable.FromEvent<StartGameCompletedEventArgs>(
h => m_Server.StartGameCompleted += h,
h => m_Server.StartGameCompleted -= h );
startObs.Subscribe( e => { m_gameState = e.EventArgs.Result.StartGameResult;
if( m_GameState.Bankroll < Game.MinimumBet )
NotifyPlayer( ... ); // some UI code here ...
TransitionVisual( GameVisualState.HandNotStarted );
} ); // above code never reached...
m_Server.StartGameAsync(); // never returns, but the WCF service is called
Solução
Consegui descobrir como fazer isso funcionar. Estou postando esta resposta no interesse de compartilhar o que aprendi.
Acontece que decidir em qual thread executar um observador inscrito é muito importante ao lidar com as chamadas do WCF do Silverlight. No meu caso, eu precisava garantir que o código subscrito seja executado no tópico da interface do usuário - que foi realizado pela seguinte mudança:
var startObs = Observable.FromEvent<StartGameCompletedEventArgs>(
h => m_Server.StartGameCompleted += h,
h => m_Server.StartGameCompleted -= h )
.Take(1) // necessary to ensure the observable unsubscribes
.ObserveOnDispatcher(); // controls which thread the observer runs on
startObs.Subscribe( e => { m_gameState = e.EventArgs.Result.StartGameResult;
if( m_GameState.Bankroll < Game.MinimumBet )
NotifyPlayer( ... ); // some UI code here ...
TransitionVisual( GameVisualState.HandNotStarted );
} ); // this code now executes with access to the UI
m_Server.StartGameAsync(); // initiates the call to the WCF service