2

チャットアプリケーションのデモをうまくフォローしました.他のいくつかのことも同様ですが、それらすべてを組み合わせても答えがないか、私が望む答えを導き出す知性が不足しているためです. シナリオは次のとおりです

でウェブサイトを構築しています

  1. MVC4
  2. .NET フレームワーク 4.0
  3. IIS7.5
  4. ASP.NET 4
  5. SQL Server 2008 R2

これは、デスクトップ アプリに付随するものです。つまり、私の Web アプリケーションとデスクトップ アプリは同じ DB と対話します。Web サイトとデスクトップ アプリケーションに AMS (Acess Management System) を追加して、アプリ内の任意の機能へのユーザー アクセス権をきめ細かく管理できるようにしたいと考えています。変更をウェブサイトにリアルタイムでプッシュしたい。

例: デスクトップ アプリのマネージャーが店員の販売レポートを表示する権利を取り消し、2 人の店員が Web サイトでオンラインだったので、これらの変更を Web サイトにプッシュする必要があります。

現在、ログイン時にユーザーの権限を保存しているため、ユーザー数が増えてもウェブサイトのパフォーマンスが急速に低下することはありません。許可されているかどうかに関係なく、すべてのアクションをチェックするときに、 DB に往復して状態を確認しますが、前述のとおり、パフォーマンスが低下します。

Web サイト自体またはデスクトップ アプリのいずれかから何らかの変更があった場合に、Web サイトに対するユーザーの権利をプッシュしたいと思いますクロック ティッカーを継続的に実行してから、接続されているすべてのクライアントに変更をブロードキャストする必要はありません。また、変更されているユーザーは、Web サイトに接続されているか、接続されていない可能性があります。誰かが私を正しい方向に向けることができますか

4

4 に答える 4

4

私はこれに対する解決策を見つけるのに多くの時間を費やしましたが、SignalR を使用して見つけた最も簡単な方法は、ハブをリポジトリ/API へのゲートウェイとして使用することです。

したがって、プロジェクトのセットアップ方法は次のとおりです。

  1. ASP.NET MVC コントローラーのアクションは、ページ全体を広げます。

    public class HomeController : Controller
    {
            //
            // GET: /Home/
            public ActionResult Index()
            {
                  return View();
            }
    }
    
  2. ビューは、ノックアウト MVVM をロードするレイアウトでラップする必要があります。ビューは、使用する必要がある MVVM の一部を初期化します (たとえば、すべての MVVM スクリプトを 1 つのファイルにまとめ、ビューは不要な接続を避けるために SignalR 接続を初期化します (以下のコードではスクリプト自体が初期化されます))。 . ビューには、KnockOut バインディングも関連付けられています。

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);
            }
         }
      }
}

実行時スクリプト (ページごとのニーズまたは構成に応じて、レイアウト、スクリプト ファイル、またはビューに配置)

$(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>

ビュー (インデックス):

@{
    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 ハブとやり取りします。ハブは呼び出しを受け取り、必要に応じて承認を確認し、呼び出しを適切なリポジトリに渡すか、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 コントローラーはデータの整合性をチェックし、コールバックを同じ 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 を実行したサイトと同じ機能を使用する必要があります。これにより、任意のクライアントからの変更が、必要な数のクライアントに伝播できるようになります (この例のようにロードしたばかりの単一のクライアント、または全員にブロードキャストするか、その間の任意の場所)。

最終的な結果として、ハブは変更/呼び出しを受け取り、API を呼び出し、API はデータを検証してハブに返します。その後、ハブはすべてのクライアントを更新できます。その後、リアルタイムのデータベースの変更とリアルタイムのクライアントの変更が正常に行われます。唯一の問題は、このシステムの外部で変更を行うと、クライアントを更新する必要があることです。つまり、すべてのクライアントの呼び出し、特に変更はハブを経由する必要があります。

さらに例が必要な場合は、喜んでいくつかお見せします。明らかに、セキュリティ対策を講じる必要があり、ここに示したコードは明らかにほんの一例です。

于 2013-12-07T06:46:05.253 に答える
1

サーバー側のイベントアグリゲーター/サービスバスの間でプロキシするライブラリを作成しました。これにより、クライアントに送信されるイベントの合理化が非常に簡単になります。ここでデモプロジェクトを見てみましょう

https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/tree/master/SignalR.EventAggregatorProxy.Demo.MVC4

デモ .sln を開くと、.NET クライアント (WPF) と JavaScript クライアントの例の両方があります。

ナゲットを使ってインストール

Install-Package SignalR.EventAggregatorProxy 

ウィキ

https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki

于 2013-09-27T09:11:53.453 に答える