gpt4 book ai didi

c# - 使用 SignalR 推送事件 "DB Data Changes"

转载 作者:行者123 更新时间:2023-11-30 20:01:23 25 4
gpt4 key购买 nike

好吧,我很好地遵循了聊天应用程序演示,还有一些其他的 tuts,但所有这些都没有回答,或者由于我缺乏智慧来得出我想要的答案。场景如下

我正在用

建立一个网站
  1. MVC 4
  2. .NET Framework 4.0
  3. IIS 7.5
  4. ASP.NET 4
  5. SQL Server 2008 R2

那是伴随桌面应用程序,即我的网络应用程序和桌面应用程序将与同一个数据库交互。我想在网站和桌面应用程序中添加一个 AMS(访问管理系统),以便可以细粒度地管理用户对应用程序内部任何功能的访问权限。我希望将更改实时推送到网站。

例如:桌面应用程序的经理撤销了职员查看销售报告的权利,并且网站上有几个职员在线,所以我希望这些更改在进行时被推送到网站。

现在我在登录时存储用户的权限,这样随着用户数量的增加不会导致网站性能快速下降,现在我知道在检查允许或不允许的每个操作时,我可以往返数据库并检查条件,但正如我之前所说,这会降低性能。

现在我想在网站本身或桌面应用程序发生一些变化时将用户的权利推送到网站,我查看了 SignalR,并在 SO 上找到了解决方案(ThisThis ),但我不希望时钟自动收报机连续运行然后将更改广播到所有连接的客户端。正在更改的用户可能已连接到网站,也可能没有。请有人能指出我正确的方向

最佳答案

我花了很多时间试图为此找到解决方案,而我发现的最简单的方法是使用 SignalR,将 Hub 用作存储库/API 的网关:

因此,项目的设置方式如下:

  1. ASP.NET MVC Controller 的操作会显示整个页面。

    public class HomeController : Controller
    {
    //
    // GET: /Home/
    public ActionResult Index()
    {
    return View();
    }
    }
  2. View 应包装在加载 Knockout MVVM 的布局中。然后 View 初始化需要使用的 MVVM 部分(例如,将所有 MVVM 脚本集中在一个文件中, View 初始化 SignalR 连接,以避免不必要的连接(下面的代码有脚本初始化本身)) .该 View 还附加了 KnockOut 绑定(bind)。​​

MVVM:

function AddressViewModel(rid, nick, line1, line2, city, state, zip)
{
<!-- Modifiable Properties should be observable. This will allow Hub updates to flow to the View -->
var self = this;
self.rid = rid;
self.nick = ko.observable(nick);
self.line1 = ko.observable(line1);
self.line2 = ko.observable(line2);
self.city = ko.observable(city);
self.state = ko.observable(new StateViewModel(state.RID, state.Title, state.Abbreviation));
self.zip = ko.observable(zip);
}
function StateViewModel(rid, title, abbreviation)
{
<!-- States are a stagnant list. These will not be updated -->
var self = this;
self.rid = rid;
self.title = title;
self.abbreviation = abbreviation;
}
var Page = new function()
{

//Page holds all Page's View Models. The init function can be modified to start only certain hubs.
var page = this;
page.init = function()
{
page.Account.init();
}
page.Account = new function ()
{
//Account holds account-specific information. Should only be retrieved on an encrypted, secure, and authorized connection.
account.init = function()
{
account.Addresses.init();
}
//Addresses manages the calls to Hubs and their callbacks to modify local content.
account.Addresses = new function ()
{
//Connect to the hub, and create an observable list.
var addresses = this;
addresses.hub = $.connection.accountAddressHub;
addresses.list = ko.observableArray([]);

//Called on initial load. This calls the Index() function on the Hub.
addresses.init = function ()
{
addresses.hub.server.index();
}
//displayMode allows for dynamic changing of the template.
addresses.displayMode = ko.computed(function ()
{
return 'Address';
});
//Empty allows to prompt user instead of just showing a blank screen.
addresses.empty = ko.computed(function ()
{
if (addresses.list().length == 0)
{
return true;
}
else
{
return false;
}
});
//During initial load, unless if MVC provides the information with the View, the list will be empty until the first SignalR callback. This allows us to prompt the user we're still loading.
addresses.loading = ko.observable(true);
//The Hub's Index function ought to reach indexBack with a list of addresses. The addresses are then mapped to the list, using the local AddressViewModel. Sets initial load to false, as we now have addresses.
addresses.hub.client.indexBack = function (addressList)
{
$.map(addressList, function (address)
{
addresses.list.push(new AddressViewModel(address.RID, address.Nick, address.Line1, address.Line2, address.City, address.State, address.ZIP));
});
addresses.loading(false);
}
}
}
}

运行脚本(放置在布局、脚本文件或 View 中,取决于每页的需要或配置)

$(function ()
{
//Configures what SignalR will do when starting, on receive, reconnected, reconnected, or disconnected.
$.connection.hub.starting(function ()
{
$('.updated').hide();
$('.updating').show();
});
$.connection.hub.received(function ()
{
$('.updating').hide();
$('.updated').show();
});
$.connection.hub.reconnecting(function ()
{
$('.updated').hide();
$('.updating').show();
});
$.connection.hub.reconnected(function ()
{
$('.updating').hide();
$('.updated').show();
});
//This will keep attempt to reconnect - the beauty of this, if the user unplugs the internet with page loaded, and then plugs in, the client reconnects automatically. However, the client would probably not receive any backlog - I haven't test that.
$.connection.hub.disconnected(function ()
{
setTimeout(function ()
{
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
//Apply knockout bindings, using the Page function from above.
ko.applyBindings(Page);
//Start the connection.
$.connection.hub.start(function ()
{
}).done(function ()
{
//If successfully connected, call the init functions, which propagate through the script to connect to all the necessary hubs.
console.log('Connected to Server!');
Page.init();
})
.fail(function ()
{
console.log('Could not Connect!');
});;
});

布局:

<!DOCTYPE html>
<html>
<head>
. . .
@Styles.Render( "~/Content/css" )
<!-- Load jQuery, KnockOut, and your MVVM scripts. -->
@Scripts.Render( "~/bundles/jquery" )
<script src="~/signalr/hubs"></script>
. . .
</head>
<body id="body" data-spy="scroll" data-target="#sidenav">
. . .
<div id="wrap">
<div class="container">
@RenderBody()
</div>
</div>
@{ Html.RenderPartial( "_Foot" ); }
</body>
</html>

View (索引):

@{
ViewBag.Title = "My Account";
}
<div>
@{
Html.RenderPartial( "_AddressesWrapper" );
}
</div>

_AddressesWrapper:

<div data-bind="with: Page.Account.Addresses">
@{
Html.RenderPartial( "_Address" );
}
<div id="Addresses" class="subcontainer">
<div class="subheader">
<div class="subtitle">
<h2>
<span class="glyphicon glyphicon-home">
</span>
Addresses
</h2>
</div>
</div>
<div id="AddressesContent" class="subcontent">
<div class="row panel panel-primary">
<!-- Check to see if content is empty. If empty, content may still be loading.-->
<div data-bind="if: Page.Account.Addresses.empty">
<!-- Content is empty. Check if content is still initially loading -->
<div data-bind="if:Page.Account.Addresses.loading">
<!-- Content is still in the initial load. Tell Client. -->
<div class="well well-lg">
<p class="text-center">
<img src="@Url.Content("~/Content/Images/ajax-loader.gif")" width="50px" height="50px" />
<strong>We are updating your Addresses.</strong> This should only take a moment.
</p>
</div>
</div>
<div data-bind="ifnot:Page.Account.Addresses.loading">
<!-- Else, if not loading, the Client has no addresses. Tell Client. -->
<div class="well well-lg">
<p class="text-center">
<strong>You have no Addresses.</strong> If you add an Addresses, you can view, edit, and delete it here.
</p>
</div>
</div>
</div>
<!-- Addresses is not empty -->
<div data-bind="ifnot: Page.Account.Addresses.empty">
<!-- We have content to display. Bind the list with a template in the Partial View we loaded earlier -->
<div data-bind="template: { name: Page.Account.Addresses.displayMode, foreach: Page.Account.Addresses.list }">
</div>
</div>
</div>
</div>
</div>
</div>

_地址:

<script type="text/html" id="Address">
<div class="col-lg-3 col-xs-6 col-sm-4 well well-sm">
<address>
<strong data-bind="text: nick"></strong><br>
<span data-bind="text: line1"></span><br>
<span data-bind="if: line2 == null">
<span data-bind="text: line2"></span><br>
</span>
<span data-bind="text: city"></span>, <span data-bind=" text: state().abbreviation"></span> <span data-bind="text: zip"></span>
</address>
</div>
</script>
  1. KnockOut 脚本与 SignalR Hub 互换。集线器接收调用、检查授权(如有必要)并将调用传递到适当的存储库或直接传递到 WebAPI 2(本示例)。 SignalR Hub 操作然后获取 API 交换的结果并确定要调用的函数以及要传递的数据。

    public class AccountAddressHub : AccountObjectHub
    {
    public override async Task Index()
    {
    //Connect to Internal API - Must be done within action.
    using( AddressController api = new AddressController(await this.Account()) )
    {
    //Make Call to API to Get Addresses:
    var addresses = api.Get();
    //Return the list only to Connecting ID.
    Clients.Client( Context.ConnectionId ).indexBack( addresses );
    //Or, return to a list of specific Connection Ids - can also return to all Clients, instead of adding a parameter.
    Clients.Clients( ( await this.ConnectionIds() ).ToList() ).postBack( Address );
    }
    }
    }
  2. API Controller 检查数据完整性并向相同的 SignalR Hub 操作发送回调。

    public class AddressController 
    : AccountObjectController
    {
    ...
    // GET api/Address
    public ICollection<Address> Get()
    {
    //This returns back the calling Hub action.
    return Account.Addresses;
    }
    ...
    }
  3. 您的 .NET 应用程序需要使用与 javascript 运行的站点相同的功能。这将允许来自任何客户端的修改然后传播到需要的许多客户端(刚刚加载的单个客户端,如本例所示,或广播给所有人,或两者之间的任何地方)

最终结果是 Hub 接收更改/调用,调用 API,API 验证数据并将其返回给 Hub。然后 Hub 可以更新所有客户端。然后,您成功地进行了实时数据库更改和实时客户端更改。唯一的问题是该系统之外的任何更改都需要客户端刷新,这意味着所有客户端调用,尤其是更改,必须通过集线器。

如果您需要更多示例,我很乐意展示一些。显然,应该采取安全措施,这里的代码显然只是一个小例子。

关于c# - 使用 SignalR 推送事件 "DB Data Changes",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19043847/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com