0

I am struggling with the observer pattern with NuGet package websocket-client (https://github.com/Marfusios/websocket-client)

The connection to the WebSocket server is stable and running.

Every request has a request ID inside the payload. The client sends it to the server and the server responds with the ID and the real data.

On the client side I need to assign every response to the corresponding request.

I thought I can do it like so:

public Task<Data> GetDataAsync()
{
    var webSocket = new WebsocketClient(Uri);
    await webSocket.Start();

    var requestId = Guid.NewGuid();

    var tcs = new TaskCompletionSource<Data>();

    var disposable = webSocket
    .MessageReceived
    .Where(message => message.Text.Contains(requestId))
    .Subscribe(message=>
    {
        var data = ParseData(message.Text);
        tcs.SetResult(data);
    });

    return tcs.Task;
}

But it actually never jumps into the subscribe method. Am I using it wrong?

BlackMatrix
  • 474
  • 5
  • 19

2 Answers2

0

I think

public Task<Data> GetDataAsync(string request)
{
    var requestId = Guid.NewGuid().ToString();

    var responseTask = WebSocket
    .MessageReceived
    .Timeout(TimeSpan.FromSeconds(5))
    .FirstOrDefaultAsync(message => message.Text.Contains(requestId));

    WebSocket.Send(request);

    var responseMessage = await responseTask;

    return ParseMessage(responseMessage);
}

is the way to go. I would even prefer SingleOrDefaultAsync instead of FirstOrDefaultAsync because there will be only one message with that request id. But that doesn't work. It always runs in the timeout.

BlackMatrix
  • 474
  • 5
  • 19
  • Please note that you've made changes to the way the code runs from your question. There was no `WebSocket.Send(request);` call in the question and you're doing `Guid.NewGuid().ToString();` but only had `Guid.NewGuid();`. The `Send` certainly changes the way I would present my answer. – Enigmativity Feb 16 '21 at 22:43
  • So is this the correct implementation to subscribe for one special message (predicate by request id) and dispose it directly after the information has been parsed? To do it asynchronly and don't effect other listeners (send and get the response)? – BlackMatrix Feb 17 '21 at 16:11
  • 1
    Often there is no such thing as a "correct implementation". Would I use this one? No. I don't like it. – Enigmativity Feb 17 '21 at 21:53
  • Any recommendations to make it better? I still have problems to pick that special message with the request id out of maybe hundreds other messages to get the corresponding response while the WebSocket is running. Best would be to not effect other subscribers or very best if other subscribers don't even have to check the message anymore when already one subscriber picked it. – BlackMatrix Feb 18 '21 at 11:05
-1

You've made your code far more complicated than it needs to me. Rx let's await an observable to get the last value produced. You can write your code like this:

public async Task<Data> GetDataAsync() =>
    await
        Observable
            .Using(
                () => new WebsocketClient(Uri),
                ws =>
                    from x in Observable.FromAsync(() => ws.Start())
                    let requestId = Guid.NewGuid()
                    from m in ws.MessageReceived
                    where m.Text.Contains(requestId)
                    select ParseData(m.Text))
            .Take(1)
            .Timeout(TimeSpan.FromSeconds(5.0));
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • That doesn't look intuitive to me. Do I need the Observable.Using block if my WebSocket is stable and running and have it as a reference as a private field? – BlackMatrix Feb 15 '21 at 13:59
  • I hoped `var requestId = Guid.NewGuid().ToString(); var responseMessageTask = Socket .MessageReceived .SingleOrDefaultAsync(message => message.Text.Contains(requestId)); Socket.Send(requestMessage); var responseMessage = await responseMessageTask; // Parse responseMessage` could have worked out. But it doesn't. – BlackMatrix Feb 15 '21 at 14:26
  • @BlackMatrix - Your existing code was creating the `Websocket` so I included the `Observable.Using`. It's just a direct translation of your code. – Enigmativity Feb 16 '21 at 00:33
  • I provided an answer with the new implementation. – BlackMatrix Feb 16 '21 at 16:01