C# Blazor 服务器:使用 INotifyPropertyChanged 显示实时数据

C# Blazor Server: Display live data using INotifyPropertyChanged

我有一个小问题,我尝试在我的 Blazor 服务器项目的页面上显示实时数据。在阅读了几篇文章后,我认为这应该可以通过使用 INotifyPropertyChanged 来实现。不幸的是我对此一窍不通,这是我尝试过的:

我使用 INotifyPropertyChanged 的​​模型价格(使用 Rider 生成):

public class Price : INotifyPropertyChanged
{
    private double _askPrice;
    private double _bidPrice;
    private double _spread;

    public Price(double askPrice, double bidPrice)
    {
        _askPrice = askPrice;
        _bidPrice = bidPrice;
    }

    public double AskPrice {
        get => _askPrice;
        set
        {
            _askPrice = value; 
            OnPropertyChanged("AskPrice");
            OnPropertyChanged("Spread");
        }
    }

    public double BidPrice
    {
        get => _bidPrice;
        set
        {
            _bidPrice = value; 
            OnPropertyChanged("BidPrice");
            OnPropertyChanged("Spread");
        }
    }

    public double Spread => _askPrice - _bidPrice;

    public event PropertyChangedEventHandler PropertyChanged;
    
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

如何获取实时数据:

public class PriceService
{
    public static Price BinancePrice;

    public static void StartBinanceStream()
    {
        var client = new BinanceSocketClient();
        // subscribe to updates on the spot API
        client.Spot.SubscribeToBookTickerUpdates("BTCEUR", data =>
        {
            BinancePrice = new Price((double)data.BestAskPrice, (double)data.BestBidPrice);
        });
    }
}

最后是我的 razorfile 的内容:

<h5>BID: @($"{PriceService.BinancePrice.BidPrice:F2} EUR")</h5>
<h5>ASK: @($"{PriceService.BinancePrice.AskPrice:F2} EUR")</h5>
<h5>Spread: @($"{PriceService.BinancePrice.Spread:F2} EUR")</h5>

@code {
protected override async Task OnInitializedAsync()
{
    PriceService.BinancePrice.PropertyChanged += async (sender, e) => { await InvokeAsync(StateHasChanged); };
    await base.OnInitializedAsync();
}

async void OnPropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
    await InvokeAsync(StateHasChanged);
}

public void Dispose()
{
    PriceService.BinancePrice.PropertyChanged -= OnPropertyChangedHandler;
}
}

它确实显示了数据,但没有实时显示变化,我需要重新打开选项卡或刷新页面才能看到当前数据。目标是 UI 每次价格变化时都会刷新。如果你能帮我解决这个问题,那就太棒了!:)

我无法真正验证问题的根源,尽管在我看来它与价格 class 和 PropertyChangedEventHandler

的调用有关

但是,下面是应该有效的代码示例:

Price.cs

public class Price : INotifyPropertyChanged
{
    private double _askPrice;
    private double _bidPrice;
    private double _spread;

    public Price(double askPrice, double bidPrice)
    {
        _askPrice = askPrice;
        _bidPrice = bidPrice;
    }

    public double AskPrice
    {
        get => _askPrice;
        set => SetProperty(ref _askPrice, value);
    }

    public double BidPrice
    {
        get => _bidPrice;
        set => SetProperty(ref _bidPrice, value);
    }

    public double Spread => _askPrice - _bidPrice;

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new 
                         PropertyChangedEventArgs(propertyName));
    }

    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string 
                                                     propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }
 } 

PriceService.cs

 public class PriceService
 {
    public Price BinancePrice { get; set; }
    double count;
    double BestAskPrice;
    double BestBidPrice;
    public PriceService()
    {
        BlazorTimer t = new BlazorTimer();
        t.SetTimer(3000, 1, 100);
        t.CountCompleted += NotifyCompleted;
        BestAskPrice = 102.09;
        BestBidPrice = 101.03;
        BinancePrice = new Price((double)BestAskPrice, (double)BestBidPrice);

    }

    private void NotifyCompleted(object sender, TimerEventArgs args)
    {
        count = (double) args.Count;

        BestAskPrice += count;
        BestBidPrice += count;
        BinancePrice.AskPrice = (double)BestAskPrice;
        BinancePrice.BidPrice =  (double)BestBidPrice;
    }
 }

注意:PriceService 是一个服务,应该添加到 DI 容器中:

services.AddSingleton<PriceService>();

另请注意,我避免使用静态内容。 注意:为了验证 UI 是否正在更改,我使用了计时器。真的很简陋,你只需要用它看东西没问题,然后应用你的代码...

BlazorTimer.cs

public class BlazorTimer
{
    private Timer _timer;
    private int count;
    private int end;
    internal void SetTimer(double interval, int start, int _end)
    {
        _timer = new Timer(interval);
        _timer.Elapsed += Counter;
        _timer.Enabled = true;
        count = start;
        end = _end;
        _timer.Start();
    }

     
    private void Counter(object sender, ElapsedEventArgs e)
    {
        count++;
        TimerEventArgs args = new TimerEventArgs { Count = count };
        OnCountCompleted(args);
      
    }
       
    protected virtual void OnCountCompleted(TimerEventArgs args)
    {
        EventHandler<TimerEventArgs> handler = CountCompleted;
        if (handler != null)
        {
            handler(this, args);
        }
    }

    public event EventHandler<TimerEventArgs> CountCompleted;
 }

 public class TimerEventArgs : EventArgs
 {
    public int Count { get; set; }
 } 

用法

@*@implements IDisposable*@
    
@inject PriceService PriceService

<h5>BID: @($"{PriceService.BinancePrice.BidPrice:F2} EUR")</h5>
<h5>ASK: @($"{PriceService.BinancePrice.AskPrice:F2} EUR")</h5>
<h5>Spread: @($"{PriceService.BinancePrice.Spread:F2} EUR")</h5>

@code {
    protected override async Task OnInitializedAsync()
    {
        PriceService.BinancePrice.PropertyChanged += async (sender, e) => { await InvokeAsync(StateHasChanged); };
        await base.OnInitializedAsync();
    }

    //async void OnPropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    //{
    //    await InvokeAsync(StateHasChanged);
    //}

    //public void Dispose()
    //{
    //    PriceService.BinancePrice.PropertyChanged -= OnPropertyChangedHandler;
    //}
}