Subscribing to an eventhandler doesn't work in abstract class using dependency injection in C# .NET 6

Viewed 35

I have an abstract class WebsocketHandler which is inherited by WsUserRequestHandler which implements an interface which in turn is injected as a singleton service to be used in other classes.

WebsocketHandler (simplified):

public abstract class WebSocketHandler
{
    private event EventHandler<OnRequestReceivedEventArgs> OnRequestReceived;
    private ClientWebSocket _client { get; set; } = new ClientWebSocket();

    protected async Task<bool> Connect(WsConnectionInfo connectionInfo)
    {
        this._client.ConnectAsync(...)
        
        while (true)
        {
            await this.Receive(this._client);
            if (this._cts.IsCancellationRequested)
                break;
        }
    }

    private async Task Receive(ClientWebSocket client)
    {
        while (true)
        {
            var result = await client.ReceiveAsync(...);
            if (result.MessageType == WebSocketMessageType.Text)
            {
                var jsonInput = Encoding.UTF8.GetString(...);
                var request = JsonConvert.DeserializeObject<IWebSocketRequest>(jsonInput, ...);

                if (request is null)
                    continue;

                // Always true
                Console.WriteLine("received request event is null: " + (this.OnRequestReceived == null));

                // Always null, so not being invoked
                this.OnRequestReceived?.Invoke(this, new OnRequestReceivedEventArgs(request)); 
            }
        }
    }

    // this method runs as expected when called and ReceivedRequest gets 
    // successfully attached to event handler (at least in the scope of the method)
    protected async Task<TRequest> RequestReceived<TRequest>(CancellationToken token) 
        where TRequest : IWebSocketRequest
    {
        var request = default(dynamic);

        void ReceivedRequest(object sender, OnRequestReceivedEventArgs args)
        {
            if (args.Request.GetType() == typeof(TRequest))
                request = args.Request;
        }

        this.OnRequestReceived += ReceivedRequest;
        Console.WriteLine(this.OnRequestReceived == null); // returns false

        while (request == null)
            await Task.Delay(25, token);

        this.OnRequestReceived -= ReceivedRequest;

        return request;
    }

The class WsUserRequestHandler is injected into another class which then calls the base method RequestReceived and awaits it. If I then, after making sure that the method is called and awaited, send a message to the websocket, the Receive method is called and OnRequestReceived turns out null. I have also made sure that the cancellation token that gets passed on to RequestReceived isn't cancelled, and also that it doesn't unsubscribe from the event.

1 Answers

I finally managed to solve it. The problem laid in the dependency injection. The class WsUserRequestHandler implements the interface IWsUserRequestHandler which inherits IWsRequestHandler which is used to inject into in a service that runs the connect method. The problem was that IWsUserRequestHandler and IWsRequestHandler instantiated two different singletons of WsUserRequestHandler instead of having them both point to the same instance.

The code responsible for the injection originally looked like this:

services.AddSingleton<IWsRequestHandler, WsUserRequestHandler>();
services.AddSingleton<IWsUserRequestHandler, WsUserRequestHandler>();

I fixed the issue by replacing it with this:

services.AddSingleton<WsUserRequestHandler>();
services.AddSingleton<IWsRequestHandler>(p=>p.GetRequiredService<WsUserRequestHandler>());
services.AddSingleton<IWsUserRequestHandler>(p=>p.GetRequiredService<WsUserRequestHandler>());
Related