SignalR with paging using Groups
Asked Answered
P

1

6

I am new to SignalR and in the learning process, I am trying to make Stock ticker. I have over 1000 stocks list and want to show paging and update on real-time changes that's why I am using Groups with SignalR. Problem is that when I open page 1 then everything works fine until I open page 2, then page 1 stop getting signals and page 2 start getting signals. What am I missing here in SignalR groups? Please help me, Thanks.

Hub

[HubName("stockTicker")]
public class StockTickerHub : Hub
{
    private StockTicker _stockTicker;

    public StockTickerHub()
        : this(StockTicker.Instance)
    {

    }

    public StockTickerHub(StockTicker stockTicker)
    {
        _stockTicker = stockTicker;
    }

    public void OpenMarket()
    {
        var page = this.Context.QueryString["page_no"];
        int pageno = Convert.ToInt32(page);
        _stockTicker.OpenMarket(pageno);
        tryAddGroup(page);
    }

    public Task tryAddGroup(string page)
    {
        return Groups.Add(Context.ConnectionId, page);
    }
}

StockTicker.cs

public class StockTicker
{

    MainModel _model = new MainModel();
    private static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
    private TimeSpan _updateInterval = TimeSpan.FromMilliseconds(3000);

    private Timer _timer;
    public volatile int pageno;
    private StockTicker(IHubConnectionContext<dynamic> clients)
    {
        Clients = clients;
    }

    public static StockTicker Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    private IHubConnectionContext<dynamic> Clients
    {
        get;
        set;
    }


    public void OpenMarket(int page_no)
    {
        pageno = page_no;
        _timer = new Timer(UpdateStockPrices, pageno, _updateInterval, _updateInterval);
    }


    private void UpdateStockPrices(object state)
    {

        var latest_stocks = new List<StockViewModel>();
        latest_stocks = _model.Get100Stocks(pageno);
        foreach (var stock in latest_stocks)
        {
            BroadcastStockPrice(stock, pageno);
        }
    }
    private void BroadcastStockPrice(StockViewModel stock, int pageno)
    {
        Clients.Group(pageno.ToString()).updateStockPrice(stock);
        //Clients.All.updateStockPrice(stock);
    }
}

(Updated Question)

StockTicker.js

if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            }
        );
    };
}

jQuery.fn.flash = function (color, duration) {
    var current = this.css('backgroundColor');
    this.animate({ backgroundColor: 'rgb(' + color + ')' }, duration / 2)
        .animate({ backgroundColor: current }, duration / 2);
};

$(function () {
    var ticker = $.connection.stockTicker;
    var $stockTable = $('#stockTable');
    var $stockTableBody = $stockTable.find('tbody');

    tdPrice = '<td data-rank-price="{currency_query}" data-sort="{currency_price_usd}"><div data-price-spn="{currency_query}">${currency_price_usd}</div></td>';
    tdPercentage = '<td data-rank-perc="{currency_query}" data-sort="{currency_change_24h_usd}"><div data-change-spn="{currency_query}"><span class="{DirectionClass}">{currency_change_24h_usd}</span></div></td>';
    tdVolume = '<td data-rank-volume="{currency_query}" data-sort="{currency_24h_volume_usd}"><div data-24-volume-spn="{currency_query}>${currency_24h_volume_usd}</div></td>';
    tdMarketcap = '<td data-rank-cap="{currency_query}" data-sort="{currency_market_cap_usd}"><div data-mcap-spn="{currency_query}">${currency_market_cap_usd}</div></td>';

    function formatStock(stock) {
        return $.extend(stock, {
            currency_price_usd: formatprices(stock.currency_price_usd),
            currency_change_24h_usd: stock.currency_change_24h_usd == null ? '0.00%' : (stock.currency_change_24h_usd).toFixed(2) + '%',
            currency_24h_volume_usd: stock.currency_24h_volume_usd == null ? '0' : nFormatter(stock.currency_24h_volume_usd, 2),
            currency_market_cap_usd: stock.currency_market_cap_usd == null ? '0' : nFormatter(stock.currency_market_cap_usd, 2),
            DirectionClass: stock.currency_change_24h_usd === 0 ? 'nochange' : stock.currency_change_24h_usd >= 0 ? 'green' : 'red'
        });
    }

    function nFormatter(num, digits) {
        var si = [
          { value: 1, symbol: "" },
          { value: 1E3, symbol: "K" },
          { value: 1E6, symbol: "M" },
          { value: 1E9, symbol: "B" },
          { value: 1E12, symbol: "T" },
          { value: 1E15, symbol: "P" },
          { value: 1E18, symbol: "E" }
        ];
        var rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
        var i;
        for (i = si.length - 1; i > 0; i--) {
            if (num >= si[i].value) {
                break;
            }
        }
        return (num / si[i].value).toFixed(digits).replace(rx, "$1") + si[i].symbol;
    }

    function formatprices(n, curr) {
        var sep = sep || ".";
        var decimals;
        if (n > 0.99999999) {
            decimals = decimals || 2;
        }
        else {
            decimals = decimals || 6;
        }

        return n.toLocaleString().split(sep)[0]
        + sep
        + n.toFixed(decimals).split(sep)[1];
    }

    $.extend(ticker.client, {
        updateStockPrice: function (stock) {
            var displayStock = formatStock(stock),
                $tdprice = $(tdPrice.supplant(displayStock)),
                $tdpercentage = $(tdPercentage.supplant(displayStock)),
                $tdvolume = $(tdVolume.supplant(displayStock)),
                $tdcap = $(tdMarketcap.supplant(displayStock));

            if (stock.LastChange != 0.0) {
                bgprice = stock.LastChange < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-price=' + stock.currency_query + ']').replaceWith($tdprice);
                $tdpricespn = $stockTableBody.find('div[data-price-spn=' + stock.currency_query + ']');
                $tdpricespn.flash(bgprice, 1500);
            }

            if (stock.LastChangePercentage != 0.0) {
                bgper = stock.LastChangePercentage < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-perc=' + stock.currency_query + ']').replaceWith($tdpercentage);
                $tdpercentagespn = $stockTableBody.find('div[data-change-spn=' + stock.currency_query + ']');
                $tdpercentagespn.flash(bgper, 1500);
            }

            if (stock.LastChangeVolume != 0.0) {
                bgvol = stock.LastChangeVolume < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-volume=' + stock.currency_query + ']').replaceWith($tdvolume);
                $tdvolumespn = $stockTableBody.find('div[data-24-volume-spn=' + stock.currency_query + ']');
                $tdvolumespn.flash(bgvol, 1500);
            }


            if (stock.LastChangeCap != 0.0) {
                bgcap = stock.LastChangeCap < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-cap=' + stock.currency_query + ']').replaceWith($tdcap);
                $tdcapspn = $stockTableBody.find('div[data-mcap-spn=' + stock.currency_query + ']');
                $tdcapspn.flash(bgcap, 1500);
            }
        }
    });

    $.connection.hub.url = 'http://localhost:13429/signalr';
    $.connection.hub.qs = { 'page_no': $("#page").val() };
    $.connection.hub.start().done(function () {
        console.log("connected");
    }).then(function () {
        return ticker.server.openMarket();
    });
});
Perimeter answered 16/3, 2018 at 19:9 Comment(6)
Are you opening the pages in new tab?Curzon
If I understand what you are wanting. I think you are approaching this the wrong way. IMHO - You should broadcast your data to each group all the time (not just when someone is viewing that page). Just start them up and push data to those groups (Page1, Page2, Page3, etc...) SignalR already handles and will not broadcast to a group without any members in it. Once you switch groups (pages in this case), you will get the next broadcast. It's hard to say anything else without seeing your client side code.Bennett
@FrankM: in that case I need to make HUBs for each page, I have done that already. But I was thinking to handle all groups from single HUB, I have seen a website who are doing from single HUB. Can I share the website? I am updating the question with Client side code.Perimeter
@FrankM: I have updated the question, Please take a look. ThanksPerimeter
You do not need a hub for each page. When the page # loads you can have it connect to the hub and join that group. The hub should already be broadcasting to every group and your data will be received. This is just one way, there are others, such as just having one page and loading all the data into json serialized string and load that data into a datatable.js (datatables.net) table, which handles your pagination within a single page.Bennett
@FrankM: I know about datatables ajax, but I don't wanna load through Jquery. I want to generate HTML for SEO purposes also. Problem is I can't figure out how to connect specific page to specific group.Perimeter
B
1

Looking at your code, your global variable pageno is always the last page that was requested:

 public void OpenMarket(int page_no)
    {
        pageno = page_no;
        _timer = new Timer(UpdateStockPrices, pageno, _updateInterval, _updateInterval);
    }

You need another variable that tracks the max page.

 public volatile int pageMax;


public void OpenMarket(int page_no)
    {
        pageno = page_no;
        pageMax = pageno > pageMax
                  : pageno
                  ? pageMax;
        _timer = new Timer(UpdateStockPrices, pageno, _updateInterval, _updateInterval);
    }

Then make sure to broadcast the correct stocks to the pages.

Barely answered 26/3, 2018 at 13:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.