Home > OS >  The difference between these two ways of subscription to an event and do I need to unsubscribe in th
The difference between these two ways of subscription to an event and do I need to unsubscribe in th

Time:11-12

I'm subscribing to the following event and I wonder what the difference between the following two examples is and do I need to unsubscribe. The library I'm using is Binance.Net by JKorf.

public event Action<Exception> Exception
{
    add => subscription.Exception  = value;
    remove => subscription.Exception -= value;
}

Example #1

// Subscribe
subscription.Data.Exception  = PortfolioService_Exception;

// Unsubscribe
subscription.Data.Exception -= PortfolioService_Exception;

private void PortfolioService_Exception(Exception ex)
{
    _logger.LogInformation($"Error: {ex.StackTrace}");
}

Example #2

If I do it this way, do I need to unsubscribe with -=?

subscription.Data.Exception  = (ex) =>
{
    _logger.LogInformation($"Error: {ex.StackTrace}");
};

Full code in addition

Snippet

private async Task<bool> InitAsync()
{
    var restClient = new BinanceClient(new BinanceClientOptions
    {
        ApiCredentials = new ApiCredentials(ApiKey, SecretKey),
        AutoTimestamp = true,
        AutoTimestampRecalculationInterval = TimeSpan.FromMinutes(30),
        TradeRulesBehaviour = TradeRulesBehaviour.AutoComply,
#if DEBUG
        LogLevel = LogLevel.Debug,
        LogWriters = new List<ILogger> { _logger } // TODO: FIX
#endif
    });

    var socketClient = new BinanceSocketClient(new BinanceSocketClientOptions
    {
        ApiCredentials = new ApiCredentials(ApiKey, SecretKey),
        AutoReconnect = true,
        ReconnectInterval = TimeSpan.FromSeconds(15),
#if DEBUG
        LogLevel = LogLevel.Debug,
        LogWriters = new List<ILogger> { _logger } // TODO: FIX
#endif
    });
    
    var listenKeyResult = await restClient.Spot.UserStream.StartUserStreamAsync();
    if (!listenKeyResult.Success)
    {
        return false;
    }

    var subscription = await socketClient.Spot.SubscribeToUserDataUpdatesAsync(listenKeyResult.Data,
        null,
        null,
        data =>
        {
            // TODO: Fix the warning
            _logger.LogInformation($"ASD {data.Data.Balances}");
        },
        data =>
        {
            // TODO: Fix the warning
            _logger.LogInformation($"BALANCE DELTA {data.Data.BalanceDelta}");
        });

    if (!subscription.Success)
    {
        return false;
    }

    // Auto unsubscribe?
    subscription.Data.Exception  = (ex) =>
    {
        _logger.LogInformation($"Error: {ex.StackTrace}");
    };

    // Subscribe
    subscription.Data.Exception  = PortfolioService_Exception;

    // Unsubscribe
    subscription.Data.Exception -= PortfolioService_Exception;

    // TODO: Put a CancellationToken logic and `StopUserStreamAsync`. and subscription.Error -=
    var keepAliveTask = Task.Run(async () =>
    {
        while (true)
        {
            // Listen key is kept alive for 60 minutes.
            await restClient.Spot.UserStream.KeepAliveUserStreamAsync(listenKeyResult.Data);
            await Task.Delay(TimeSpan.FromMinutes(50));
        }
    });
    
    return true;
}

private void PortfolioService_Exception(Exception ex)
{
    _logger.LogInformation($"Error: {ex.StackTrace}");
}

The library

using CryptoExchange.Net.Objects;
using System;
using System.Threading.Tasks;

namespace CryptoExchange.Net.Sockets
{
    /// <summary>
    /// Subscription to a data stream
    /// </summary>
    public class UpdateSubscription
    {
        private readonly SocketConnection connection;
        private readonly SocketSubscription subscription;

        /// <summary>
        /// Event when the connection is lost. The socket will automatically reconnect when possible.
        /// </summary>
        public event Action ConnectionLost
        {
            add => connection.ConnectionLost  = value;
            remove => connection.ConnectionLost -= value;
        }

        /// <summary>
        /// Event when the connection is closed. This event happens when reconnecting/resubscribing has failed too often based on the <see cref="SocketClientOptions.MaxReconnectTries"/> and <see cref="SocketClientOptions.MaxResubscribeTries"/> options,
        /// or <see cref="SocketClientOptions.AutoReconnect"/> is false
        /// </summary>
        public event Action ConnectionClosed
        {
            add => connection.ConnectionClosed  = value;
            remove => connection.ConnectionClosed -= value;
        }

        /// <summary>
        /// Event when the connection is restored. Timespan parameter indicates the time the socket has been offline for before reconnecting. 
        /// Note that when the executing code is suspended and resumed at a later period (for example laptop going to sleep) the disconnect time will be incorrect as the diconnect
        /// will only be detected after resuming. This will lead to an incorrect disconnected timespan.
        /// </summary>
        public event Action<TimeSpan> ConnectionRestored
        {
            add => connection.ConnectionRestored  = value;
            remove => connection.ConnectionRestored -= value;
        }

        /// <summary>
        /// Event when the connection to the server is paused based on a server indication. No operations can be performed while paused
        /// </summary>
        public event Action ActivityPaused
        {
            add => connection.ActivityPaused  = value;
            remove => connection.ActivityPaused -= value;
        }

        /// <summary>
        /// Event when the connection to the server is unpaused after being paused
        /// </summary>
        public event Action ActivityUnpaused
        {
            add => connection.ActivityUnpaused  = value;
            remove => connection.ActivityUnpaused -= value;
        }

        /// <summary>
        /// Event when an exception happens during the handling of the data
        /// </summary>
        public event Action<Exception> Exception
        {
            add => subscription.Exception  = value;
            remove => subscription.Exception -= value;
        }

        /// <summary>
        /// The id of the socket
        /// </summary>
        public int SocketId => connection.Socket.Id;

        /// <summary>
        /// The id of the subscription
        /// </summary>
        public int Id => subscription.Id;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="connection">The socket connection the subscription is on</param>
        /// <param name="subscription">The subscription</param>
        public UpdateSubscription(SocketConnection connection, SocketSubscription subscription)
        {
            this.connection = connection;
            this.subscription = subscription;
        }
        
        /// <summary>
        /// Close the subscription
        /// </summary>
        /// <returns></returns>
        public Task CloseAsync()
        {
            return connection.CloseAsync(subscription);
        }

        /// <summary>
        /// Close the socket to cause a reconnect
        /// </summary>
        /// <returns></returns>
        internal Task ReconnectAsync()
        {
            return connection.Socket.CloseAsync();
        }

        /// <summary>
        /// Unsubscribe a subscription
        /// </summary>
        /// <returns></returns>
        internal async Task UnsubscribeAsync()
        {
            await connection.UnsubscribeAsync(subscription).ConfigureAwait(false);
        }

        /// <summary>
        /// Resubscribe this subscription
        /// </summary>
        /// <returns></returns>
        internal async Task<CallResult<bool>> ResubscribeAsync()
        {
            return await connection.ResubscribeAsync(subscription).ConfigureAwait(false);
        }
    }
}

CodePudding user response:

Example #1 and Example #2 do exactly the same thing. The only difference is that in Example #1 your handler has a name while in Example #2 it does not have a name (i.e. is anonymous). Because it has no name, it is difficult to unregister the handler with -=, as you seem to have figured out.

If you want to be able to use a lambda but you also want to be able to unsubscribe, you can assign the lambda to a variable first.

Action<Exception> tmp = ex => _logger.LogInformation($"Error: {ex.StackTrace}");
subscription.Data.Exception  = tmp;
subscription.Data.Exception -= tmp; //Unsubscribe
  • Related