如何实现网页的实时数据

mas*_*son 17 c# asp.net ajax signalr asp.net-web-api

(这是一个Q/A风格的问题,旨在成为那些提出类似问题的人的首选资源.很多人似乎偶然发现了这样做的最佳方式,因为他们不知道所有的选择许多答案都是ASP.NET特有的,但AJAX和其他技术确实在其他框架中具有等价物,例如socket.io和SignalR.)

我有一个我在ASP.NET中实现的数据表.我想实时或接近实时地显示页面上此基础数据的更改.我该怎么办呢?

我的型号:

public class BoardGame
    {
    public int Id { get; set;}
    public string Name { get; set;}
    public string Description { get; set;}
    public int Quantity { get; set;}
    public double Price { get; set;}

    public BoardGame() { }
    public BoardGame(int id, string name, string description, int quantity, double price)
        {
        Id=id;
        Name=name;
        Description=description;
        Quantity=quantity;
        Price=price;
        }
    }
Run Code Online (Sandbox Code Playgroud)

代替这个例子的实际数据库,我只是将数据存储在Application变量中.我将在我Application_Start的Global.asax.cs函数中播种它.

var SeedData = new List<BoardGame>(){
    new BoardGame(1, "Monopoly","Make your opponents go bankrupt!", 76, 15),
    new BoardGame(2, "Life", "Win at the game of life.", 55, 13),
    new BoardGame(3, "Candyland", "Make it through gumdrop forrest.", 97, 11)
    };
Application["BoardGameDatabase"] = SeedData;
Run Code Online (Sandbox Code Playgroud)

如果我使用Web窗体,我会用转发器显示数据.

<h1>Board Games</h1>
        <asp:Repeater runat="server" ID="BoardGameRepeater" ItemType="RealTimeDemo.Models.BoardGame">
            <HeaderTemplate>
                <table border="1">
                    <tr>
                        <th>Id</th>
                        <th>Name</th>
                        <th>Description</th>
                        <th>Quantity</th>
                        <th>Price</th>
                    </tr>
            </HeaderTemplate>
            <ItemTemplate>
                <tr>
                    <td><%#: Item.Id %></td>
                    <td><%#: Item.Name %></td>
                    <td><%#: Item.Description %></td>
                    <td><%#: Item.Quantity %></td>
                    <td><%#: Item.Price %></td>
                </tr>
            </ItemTemplate>
            <FooterTemplate></table></FooterTemplate>
        </asp:Repeater>
Run Code Online (Sandbox Code Playgroud)

并在后面的代码中加载该数据:

protected void Page_Load(object sender, EventArgs e)
    {
    BoardGameRepeater.DataSource = Application["BoardGameDatabase"];
    BoardGameRepeater.DataBind();
    }
Run Code Online (Sandbox Code Playgroud)

如果这是使用Razor的MVC,它只是模型的一个简单的foreach:

@model IEnumerable<RealTimeDemo.Models.BoardGame>
<h1>Board Games</h1>
<table border="1">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Id)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Description)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Quantity)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Price)
        </th>
    </tr> 
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Id)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Description)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Quantity)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
    </tr>
} 
</table>
Run Code Online (Sandbox Code Playgroud)

让我们使用Web窗体创建一个用于添加数据的页面,以便我们可以实时观察数据更新.我建议您创建两个浏览器窗口,以便您可以同时查看表单和表格.

<h1>Create</h1>
<asp:Label runat="server" ID="Status_Lbl" /><br />
Id: <asp:TextBox runat="server" ID="Id_Tb" /><br />
Name: <asp:TextBox runat="server" ID="Name_Tb" /><br />
Description: <asp:TextBox runat="server" ID="Description_Tb" /><br />
Quantity: <asp:TextBox runat="server" ID="Quantity_Tb" /><br />
Price: <asp:TextBox runat="server" ID="Price_Tb" /><br />
<asp:Button runat="server" ID="SubmitBtn" OnClick="SubmitBtn_Click" Text="Submit" />
Run Code Online (Sandbox Code Playgroud)

而背后的代码:

protected void SubmitBtn_Click(object sender, EventArgs e)
    {
    var game = new BoardGame();
    game.Id = Int32.Parse(Id_Tb.Text);
    game.Name = Name_Tb.Text;
    game.Description = Description_Tb.Text;
    game.Quantity = Int32.Parse(Quantity_Tb.Text);
    game.Price = Int32.Parse(Price_Tb.Text);
    var db = (List<BoardGame>)Application["BoardGameDatabase"];
    db.Add(game);
    Application["BoardGameDatabase"] = db;
    //only for SignalR
    /*var context = GlobalHost.ConnectionManager.GetHubContext<GameHub>();
    context.Clients.All.addGame(game); */           
    }
Run Code Online (Sandbox Code Playgroud)

mas*_*son 19

SignalR

这是我最乐于分享的答案,因为它代表了一种更清晰的实现,它是轻量级的,并且在当今的移动(数据受限)环境中运行良好.

多年来,有几种方法可以提供从服务器到客户端的数据"实时"推送(或推送数据的外观).快速短轮询(类似于我基于AJAX的答案),长轮询,永久帧,服务器发送事件WebSocket是用于实现此目的的不同传输机制.SignalR是一个抽象层,能够根据客户端和服务器的功能选择合适的传输机制.使用SignalR最好的部分是它很简单.您不必担心传输机制,并且编程模型易于理解.

我将定义一个SignalR集线器,但只是留空.

public class GameHub : Hub
    {
    }
Run Code Online (Sandbox Code Playgroud)

当我向"数据库"添加数据时,我将运行下面的代码.如果您阅读了该问题,您会看到我在"创建"表单中对其进行了评论.你会想要取消注释.

var context = GlobalHost.ConnectionManager.GetHubContext<GameHub>();
context.Clients.All.addGame(game);
Run Code Online (Sandbox Code Playgroud)

这是我的页面代码:

<h1>SignalR</h1>
        <asp:Repeater runat="server" ID="BoardGameRepeater" ItemType="RealTimeDemo.Models.BoardGame">
            <HeaderTemplate>
                <table border="1">
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Name</th>
                            <th>Description</th>
                            <th>Quantity</th>
                            <th>Price</th>
                        </tr>
                    </thead>
                    <tbody id="BoardGameTblBody">
            </HeaderTemplate>
            <ItemTemplate>
                <tr>
                    <td><%#: Item.Id %></td>
                    <td><%#: Item.Name %></td>
                    <td><%#: Item.Description %></td>
                    <td><%#: Item.Quantity %></td>
                    <td><%#: Item.Price %></td>
                </tr>
            </ItemTemplate>
            <FooterTemplate></tbody></table></FooterTemplate>
        </asp:Repeater>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
        <script src="Scripts/jQuery-1.6.4.min.js"></script>
        <script src="Scripts/jquery.signalR-2.1.1.min.js"></script>
        <script src="signalr/hubs"></script>
        <script type="text/javascript">
            var hub = $.connection.gameHub;
            hub.client.addGame = function (game) {
                $("#BoardGameTblBody").append("<tr><td>" + game.Id + "</td><td>" + game.Name + "</td><td>" + game.Description + "</td><td>" + game.Quantity + "</td><td>" + game.Price + "</td></tr>");
            };
            $.connection.hub.start();
        </script>
Run Code Online (Sandbox Code Playgroud)

而背后的代码:

protected void Page_Load(object sender, EventArgs e)
        {
        BoardGameRepeater.DataSource = Application["BoardGameDatabase"];
        BoardGameRepeater.DataBind();
        }
Run Code Online (Sandbox Code Playgroud)

注意这里发生了什么.当服务器调用context.Clients.All.addGame(game);它时,hub.client.addGame为每个连接到GameHub的客户端执行已分配的功能.SignalR负责为我布线事件,并自动将game服务器上的game对象转换为客户端上的对象.最重要的是,每隔几秒就没有来回的网络流量,所以它非常轻巧.

好处:

  • 网络流量非常轻
  • 易于开发,但仍然灵活
  • 不发送带有请求的viewstate
  • 不连续轮询服务器.

注意,您可以在客户端添加一个功能,editedGame以便将更改的数据轻松推送到客户端(同样用于删除).