Search code examples
asp.net-mvcsignalrheartbeat

Tracking online users in signalr: where I should create instance of PresenceManager and how to pass the parameter heartbeat to it


I read this topic which is about enhancing the method for tracking online users using database. In short, the method use the following database :

public class ConnectedClient
{
    public long ID { get; set; }
    [StringLength(100)]
    public string ConnectionId { get; set; }
    public ConnectionStatus Status { get; set; }
    public string ApplicationUserID { get; set; }
    public string UserAgent { get; set; }
    public DateTimeOffset LastActivity { get; set; }

    //Relationship
    public ApplicationUser ApplicationUser { get; set; }
}

And for tracking online users :

public class PresenceManager
    {
        private const int zombiePeriodThreshold = 2;
        private readonly ITransportHeartbeat _heartbeat;
        private readonly TimeSpan _checkInterval = TimeSpan.FromSeconds(60);
        private readonly int _zombieThreshold;
        private Timer timer;

        public PresenceManager(ITransportHeartbeat heartbeat)
        {
            _heartbeat = heartbeat;
            _zombieThreshold = (int)_checkInterval.TotalSeconds * zombiePeriodThreshold;
        }

        public void StartMonitoring()
        {
            if (timer == null)
            {
                timer = new Timer(t =>
                {
                    try
                    {
                        Check();
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceError(ex.Message);
                    }
                }, null, TimeSpan.Zero, _checkInterval);
            }
        }

        //EF6 adds much better support for contains in queries http://bit.ly/1wmQ2VQ
        private void Check()
        {
            using (var db = new ApplicationContext())
            {
                //Update active connections
                var activeConnections = _heartbeat.GetConnections().Where(b => b.IsAlive).Select(b => b.ConnectionId);
                db.ConnectedClients
                    .Where(c => activeConnections.Contains(c.ConnectionId))
                    .ToList()
                    .ForEach(c => c.LastActivity = DateTimeOffset.UtcNow);

               //Remove zombie connections
               var zombies = db.ConnectedClients.Where(c => SqlFunctions.DateDiff("ss", c.LastActivity, DateTimeOffset.UtcNow) >= _zombieThreshold).ToList();
               var zombieUserIDs = zombies.Select(b => b.ApplicationUserID);
               db.Users
                   .Where(c => zombieUserIDs.Contains(c.Id))
                   .Where(c => c.ConnectedClients.Count <= 1)
                   .ToList()
                   .ForEach(u => u.Status = UserStatus.Offline);

                db.ConnectedClients.RemoveRange(zombies);
                db.SaveChanges();
            }
        }

    }

I understand all the code above, the only thing that I don’t know is where I should create instance of PresenceManager and how to pass the parameter heartbeat to it.


Solution

  • Take a look at the answer provided here.

    ASP.NET MVC OWIN and SignalR - two Startup.cs files

    While the question is not directly related to yours they inadvertently asnwered where to create instance of PresenceManager and how to pass the parameter heartbeat to it:

    public void ConfigureSignalR(IAppBuilder app) {
        var heartBeat = GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>(); 
        var monitor = new PresenceMonitor(heartBeat); 
        monitor.StartMonitoring();
        // Any connection or hub wire up and configuration should go here 
        app.MapSignalR();
    }
    

    And then call it from the main Start up like so

    public void Configuration(IAppBuilder app) {
        ConfigureAuth(app);
        ConfigureSignalR(app);
    }