使用WebMatrix的ASP.NET网页中的信号r和knownout

SignalR是一个库,可简化Web服务器和客户端之间的持久连接的创建和管理。这有助于开发可以实时显示在服务器上保存的数据的更新。聊天应用是本技术中最明显的受益者,但需要向用户提供可用性的业务范围应用程序也可以使用。在这里,我看看延长了 规范信号聊天示例 to incorporate a "who's typing"功能,我也延长了我的 以前的淘汰赛例子 to use SignalR.

首先,SignalR设计用于解决的问题是什么? Web工作在请求响应模型上。浏览器和其他用户代理使请求和Web服务器提供对该请求的响应。响应被发送到用户代理中请求中提供的递送地址。这是网络 - 服务器上的东西的自然顺序,不能在没有请求的情况下做出反应。在大多数情况下,这不是一个问题,但如果要在网页上显示实时更新,则需要使用AJAX反复轮询服务器的技术,以查看是否已对数据进行任何更改。或者,您可以使用 彗星技术,它在服务器和客户端之间保持持久连接。 HTML5推出了两种新技术 - 服务器发送事件和WebSockets。 SignalR是一个用户友好的包装器,周围所有这些技术都使得创建需要实时显示数据的应用程序更容易。 SignalR使用它可用的HTML5 Web Sockets API,并返回到它们不是 - 服务器发送事件,永远帧或长轮询的其他技术,其中的最后两种是Comet技术。

signalr. 目前处于预发布 - 释放候选人1的精确。它通过Nuget或GitHub提供。但是,由于它是预发布,因此它不通过WebMatrix Nuget客户端提供。但是,如果您选择,您可以使用Visual Studio(并为Web版本表示)"Include Prerelease"而不是默认"Stable Only"选项。或者您可以使用Package Manager控制台并只需键入:

安装程序包microsoft.aspnet.signalr -pre

建议以这种方式(通过Nuget)安装包装,因为要安装相当多的位和碎片:

 signalr.

信号的中心块是集线器,其类似于ASP.NET MVC中的控制器。集线器负责接收输入和生成输出。集线器被写为继承的服务器端类 microsoft.aspnet.signalr.hubs.hub.hub.hub.。在集线器类中创建的公共方法旨在从客户端代码调用。它们通常会导致发送给客户端的响应。以下是ASP.NET网站的聊天示例的集线器和方法:

 使用  Microsoft.aspnet.signalr.hubs.;

public class  ch  : Hub
{
    public void  发送 (string name, string message)
    {
        客户端.All.BroadcastMessage.(name, message);
    }
}

这应该保存为app_code中的chathub.cs(C#类文件)。一种 使用 指令将添加到文件的顶部 Microsoft.aspnet.signalr.hubs. 可供代码。在运行时,动态生成JavaScript文件,其中包含公众的客户端实现 发送 方法使其可以充当服务器端方法的代理。你写了一个实施 广播消息 在客户端代码中的方法,使得集线器方法可以调用它,从而提供响应。您还可以确定谁应该通过可用的选项接收响应:

 signalr.

这是与布局页面开始的完整客户端代码:

@{
    
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@Page.Title</title>
        <link href="~/Styles/site.css" rel="stylesheet" type="text/css" />
        <script src="~/Scripts/jquery-1.6.4.min.js" ></script>
        <script src="~/Scripts/jquery.signalR-1.0.0-rc1.min.js"></script>
        <script src="~/signalr/hubs"></script>
        @RenderSection("script", required: false)
    </head>
    <body>
        @RenderBody()
    </body>
</html>

请注意,对调用脚本有一个引用 在一个 signalr. 文件夹,既不存在。这是动态生成脚本的占位符,该脚本将集线器方法转换为客户端代码。

@{

}

    <div class="container">
        <input type="text" id="message" />
        <input type="button" id="sendmessage" value="Send" />
        <input type="hidden" id="displayname" />
        <ul id="discussion"></ul>
    </div>

@section script{
    <script type="text/javascript">
        $(function () {
 
            var chat = $.connection.chatHub;

            chat.client.broadcastMessage. = function (name, message) {
                $('#discussion').append('<li><strong>' + name
                    + '</strong>:&nbsp;&nbsp;' + message + '</li>');
            };

            
            $('#displayname').val(prompt('Enter your name:', '')); 
            $('#message').focus();
            $.connection.hub.start().done(function () {
                $('#sendmessage').click(function () {
                    var encodedName = $('<div />').text($('#displayname').val()).html();
                    var encodedMsg = $('<div />').text($('#message').val()).html();
                    chat.server.send(encodedName, encodedMsg);
                    $('#message').val('').focus();
                });
            });
        });
    </script>
}

某些HTML元素在文件的顶部创建 - 文本框,按钮,隐藏字段和无序列表。在脚本块中,为服务器端创建客户端代理 ch class:

var chat = $.connection.chatHub;

集线器的客户端版本使用驼壳(第一字小写)生成。请记住,您必须编写自己的Commity的客户端实现 广播消息 方法?下一节显示。它需要由集线器方法提供的两个字符串,并使用它们生成添加到无序列表中的列表项。通过提示存储在隐藏字段中的名称,并将焦点设置在文本框中的名称,设置了下一条线。然后打开与集线器的连接,然后将单击事件处理程序添加到导致的按钮中 发送 调用集线器的服务器版本中的方法:

chat.server.send.send.(encodedName, encodedMsg);

所以只要重新回顾,按钮单击调用客户端 chat.server.send.send. 方法,拨打公众 chathub.send.send. 方法,响应的方法 客户端.All.BroadcastMessage. 它调用客户端 chat.client.broadcastMessage. function.

获取此工作需要的最终事项,即用于注册集线器的默认路由,这是在_APPStart.cshtml文件中最好的:

@ 使用  System.Web.Routing
@ 使用  Microsoft.AspNet.SignalR

@{
    RouteTable.Routes.MapHubs();
}

聊天示例很容易理解,并说明基本工作流。但它不是任何想象力的完整申请。它需要更多,并且聊天应用程序的共同特征是当前键入的指标。我们可以使用上面的工作流逻辑计划实现。

客户端上的东西需要调用服务器端方法。击键是键入的态度,所以这将是:

$('#message').keypress(function () {
    var encodedName = $('<div />').text($('#displayname').val()).html();
    chat.server.isTyping(encodedName);
});

从客户方法的定义,应该易于辨别所需方法的名称和签名 ch class:

public void  在打字 (string name){
    Clients.All.sayWhoIsTyping(name);
}

通过每个按键,当前用户的名称将传递给服务器端 在打字 通过调用调用的客户端函数来响应方法 Saywhoistyping:

chat.client.sayWhoIsTyping = function (name) {
    $('#isTyping').html('<em>' + name + ' is typing</em>');
    setTimeout(function () { 
        $('#isTyping').html('&nbsp;');
    }, 3000);
};

它是一个粗略且准备好的功能,它取出了从服务器端方法传递的名称,并将其显示为具有ID的DIV中的字符串的一部分 在打字 。 3秒后文本被清除。

信号敲除了

我有 以前写的淘汰赛。如果您不确定淘汰赛是什么,您应该先阅读上一篇文章。在上一篇文章中,该示例显示了一个简单的应用程序,其中用户可以选择产品,从生成的列表中选择一个产品以查看详细信息。有一个简单的按钮,导致ViewModel的 UniteInstock. 概述的价值减少,并且由于购买而被更新的UI。这是淘汰淘汰的好处之一 - ViewModel中可观察值的变化导致在UI中绑定的情况下反映的变化。但更改已本地化为个人用户的浏览器。如果实际购买产品单元,或者确实可以提供更多,如果所有当前用户通知更改,则这将是一个非常好的主意 UniteInstock. 实时,对吗?声音就像信号态的任务。

现有淘汰示例代码所需的更改次数实际上非常小。我修改了现有的视图 功能并添加另一个功能来管理重新排序的产品。我还添加了两个计算的可观察到(它们曾经被称为相关的观察);一个操作用户是否可以根据可用性购买产品,而另一个方法是否已达到重新排序级别:

viewModel.buy = function() {
    var id = this.productId();
    ProductShub.Server.BuyProduct.(id);
};

viewModel.reorder = function() {
    var quantity = this.reorderQuantity();
    var id = this.productId();
    ProductShub.Server.ReorderProduct.(quantity, id);
};

viewModel.canBuy = ko.computed(function() {
    return this.unitsInStock() > 0;
}, viewModel);

viewModel.canReorder = ko.computed(function() {
    return this.unitsInStock() <= this.reorderLevel();
}, viewModel);

我还向ViewModel添加了一个ReorderLevel属性,然后添加了一个文本框和按钮进行重新排序,并将其与其可见性联系起来 笨拙 计算可观察。如果库存单位达到重新排序级别值,则重新排序按钮和文本框将仅可见。这 买一个 如果有库存单位,则只能启用按钮。以前,当没有库存单位时,点击的结果 买一个 是一个警报,表明没有股票存在:

<div class="row" data-bind="visible: canReorder">
    <span class="label"><input data-bind="value: reorderQuantity" class="reorder" /></span>
    <span><button data-bind="click: reorder">Reorder</button></span>
</div>
<div class="row">
    <span class="label">&nbsp;</span>
    <span><button data-bind="click: buy, enable: canBuy"> 买一个 </button></span>
</div>

如果您沿途一直在关注,您可以看到两个ViewModel功能: 重新订购 指向当前不存在的服务器端集线器方法: 卖出产品 (表示 ProductShub.Server.BuyProduct. ) 和 重新订购产品 (表示 ProductShub.Server.ReorderProduct.)。所以这里是ProductHub的代码:

 使用  Microsoft.aspnet.signalr.hubs.;
 使用   WebMatrix. .Data;

public class ProductHub : Hub
{
    public void  卖出产品 (int productId) {
         使用  (var db = Database.Open("Northwind")) {
            var  UniteInstock.  = db.QueryValue("SELECT UnitsInStock FROM Products WHERE ProductId = @0", productId);
            if (unitsInStock > 0) {
                db.Execute("UPDATE Products Set UnitsInStock = UnitsInStock - 1 WHERE ProductId = @0", productId);
                unitsInStock -= 1;
            }
            Clients.All.updateAvailableStock(unitsInStock, productId);
        }
    }

    public void 重新订购产品(int quantity, int productId){
         使用  (var db = Database.Open("Northwind")) {
            db.Execute("UPDATE Products Set UnitsInStock = UnitsInStock + @0 WHERE ProductId = @1", quantity, productId);
            var  UniteInstock.  = db.QueryValue("SELECT UnitsInStock FROM Products WHERE ProductId = @0", productId);
            Clients.All.updateAvailableStock(unitsInStock, productId);
        }
    }
}

这一次,由用户操作引起的更改将存储在数据库中,在数据库中不会发生在上一个聊天示例中。这 卖出产品 方法检索所选择的产品的当前单位数,并且如果存在任何情况,则允许购买。经过股票的经过修订的单位数,以及产品ID通过A.通过A组发送到所有当前连接的客户端 UpdateAvailableStock. 方法。 这 重新订购产品 方法只需通过ViewModel函数传递到方法中的任何数量即可增加单元金属值。再次,库存中的订正数量为库存为所有连接的客户端。

那么客户如何处理这个广播?它需要一个 UpdateAvailableStock. 功能使集线器方法称为:

productHub.client.updateAvailableStock = function(unitsInStock, productId) {
    if (viewModel.productId() == productId) {
        viewModel.unitsInStock(unitsInStock);
        $('span[data-bind="text: unitsInStock"]').effect('highlight', { color: '#9ec6e2' }, 3500);
    }
};

首先是检查更新产品的ID是否是当前绑定到ViewModel的ID,如果是,则更新值,并且将突出显示效果应用于显示单位的跨度:

 signalr.

剩余的待待的是为ProductHub创建一个客户端代理并建立连接:

productsHub = $.connection.productHub;

$.connection.hub.start();

这两个例子都可以作为可用的样本场所的一部分提供 在GitHub. .

signalr. 比这些介绍演示更强大。您可以编写自己的PipelIneModule类并将自己的处理代码注入集线器管道。您可以使用Data Annotization Style属性将授权确切地添加到集线器方法或完整的集线器中。你可以 阅读更多关于此处的最新信号的更多信息.