C#項目集成SignalR

1、什麼是SugnalR?

       ASP. NET Signal是爲ASP.NET開發人員提供的一個庫,可以簡化開發人員將實時Web功能添加到應用程序的過程。實時Web功能是指這樣一種功能:當所連接的客戶端變得可用時服務器代碼可以立即向其推送內容,而不是讓服務器等待客戶端請求新的數據。

       我個人的理解是:當客戶端向服務器端發起一次請求,建立連接之後,當服務器端數據發生了變化,無需客戶端再次發起請求,服務器端即可將更新後的數據主動推送給客戶端。實際工作中可以用它做很多事情,如果用戶是通過刷新web頁面,來查看新的數據,或者是通過頁面實現長輪詢來檢索新的數據,那麼就該考慮使Signal了。示例包括儀表板和監視應用程序、協作應用程序(例如同時編輯文檔)、工作進度更新和實時表單等等。

       Signal會自動管理連接,並允許您像Chat室那樣向所有連接的客戶端同時發送消息。你也可以向特定的客戶端發送消息。客戶端和服務器之間的連接是持久性的,不像傳統的HITP連接每個通信都需要重新建立一個連接Signal支持“服務器推送”功能,即服務器代碼可以使用遠程過程調用(PRC)來調用瀏覽器中的客戶端代碼,而不使用目前在Web上常用的請求ー響應模型Signal應用程序可以通過使用服務總線。SQL Server或 Redis擴展到數以千計的客戶端Signal是開源的,可以通過 Github訪問。

2、爲什麼要用SignalR?(我的使用場景)

由於VFS系統功能邏輯較爲複雜,某些功能操作後等待時間較長,可以運用SignalR來減少使用者在等待返回結果上的時間浪費。例如:對賬單刷新功能,此功能需要刷新報表中的相關結算單位的當月的對賬單明細、對賬單彙總的數據,由於報表的邏輯較爲複雜,因此如果結算單位單量很大的情況下刷新的過程比較長,因此引入SignalR,當客戶端運行時會首先與服務器建立連接,發起對賬單刷新請求時,會異步執行數據的刷新操作,異步執行的過程中會直接返回“系統正在刷新數據”的通知消息,此時,使用者不必在此等候,可先去做其他工作,等數據刷新執行完畢後,服務器會主動向客戶端發起消息通知,通知客戶端對賬單刷新已完成。

3、怎麼用SignalR?

Demo下載地址(可點擊此處下載

服務器端:

(1)在項目文件夾中新建項目,命名爲SignalR

注意:新建的SignalR的目標框架要與原有的框架版本一致

(2)在此項目中安裝SignalR相關的nuget包:

Microsoft.AspNet.SignalR/Microsoft.AspNet.SignalR.Core/Microsoft.AspNet.SignalR.JS等

(3)在Startup.cs項目啓動文件中加入SignalR的啓動配置

app.Map("/signalrapi", map =>
            {
                // 跨域支持
                map.UseCors(CorsOptions.AllowAll);
                var hubConfiguration = new HubConfiguration
                {

                };
                map.RunSignalR(hubConfiguration);
            });

注:需要引入包using Microsoft.AspNet.SignalR; 和  using Microsoft.Owin.Cors;

(4)在新建的SingnalR項目中新建文件夾MsgHubs,並在此文件夾下新建類MsgHub,此類需集成Hub類(Microsoft.AspNet.SignalR.Hub)。此類用於寫客戶端與服務器端交互的公用方法,例如:客戶端啓動或刷新時調用服務器端記錄當前登陸人信息

using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalR.MsgHubs
{
    public class MsgHub : Hub
    {
        public static dynamic client { get; set; }
        public static Dictionary<string, string> _clients = new Dictionary<string, string>();

        /// <summary>
        /// 客戶端調用服務器,記錄用戶唯一標識
        /// </summary>
        /// <param name="message"></param>
        public void ClientToServer(string message)
         {
            if (!_clients.ContainsKey(message))
            {
                _clients.Add(message, Context.ConnectionId);
            }
            else
            {
                _clients.Remove(message);
                _clients.Add(message, Context.ConnectionId);
            }
        }

    }
}

(5)新建的項目中都會默認有一個Class類,可將其重命名用於寫向指定用戶發送請求的公用方法

using Lunz.Services;
using Microsoft.AspNet.SignalR;
using SignalR.MsgHubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalR
{
    public class MsgHelper
    {
        private static IHubContext _chat;

        public static IHubContext Chat
        {
            get
            {
                if (_chat == null)
                {
                    _chat = GlobalHost.ConnectionManager.GetHubContext<MsgHub>();
                }
                return _chat;
            }
        }

        private static MsgHelper _current;

        public static MsgHelper Current
        {
            get
            {
                if (_current == null)
                {
                    _current = new MsgHelper();
                }
                return MsgHelper._current;
            }
        }

        /// <summary>
        /// 向指定用戶發送消息
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public WebApiResult Send(string userName, MessageInfo msgInfo)
        {
            var result = new WebApiResult();
            try
            {
                //此處需引入MsgHub類
                if (MsgHub._clients.ContainsKey(userName))
                {
                    var connectId = MsgHub._clients[userName];
                   //發送到指定客戶端
                    //讓客戶端獲取最新的消息
                    Chat.Clients.Client(connectId).getNewMessage(msgInfo);
                    //Chat.Clients.Client(connectId).serverToClient(msgInfo+"  "+DateTime.Now.ToString("yyyy-MM-dd"));
                    //發送到所有客戶端
                    //Clients.All.serverToClient(message + "  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                }
            }
            catch
            {
            }
            return result;
        }
    }
}


public class MessageInfo
{
    public int Id { get; set; }
    public string Message { get; set; }
    public Nullable<System.DateTime> SendAt { get; set; }
    public Nullable<System.Guid> ReceiveUserId { get; set; }
    public string ReceiveUserName { get; set; }
    public string ReceiveUserCode { get; set; }
    public bool IsRead { get; set; }
    public Nullable<System.DateTime> ReadTime { get; set; }
    public Nullable<System.DateTime> InsertTime { get; set; }
    public bool Deleted { get; set; }
}

(6)新建Ctroller&Service專門用於消息的記錄與發送

例如:對賬單刷新

/// <summary>
/// 對賬單刷新
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[ActionName("BillInfoRefresh")]
[HttpPost]
public WebApiResult BillInfoRefresh (CustConfirmDetailModel model)
{
    var result = new WebApiResult();
    //異步執行,執行完畢後會將執行結果反饋到客戶端
    _messageService.BillInfoRefreshTast(model);
    return result;
}

注:異步方法無法獲取HttpContext.Current,此值爲空,在獲取CurrentUser信息時會報錯,因此,此處可將異步方法寫在方法體內

public void BillInfoRefreshTast(CustConfirmDetailModel model)
        {
            var result = new WebApiResult();
            MessageModel message = new MessageModel();
            List<sys_message> messageList = new List<sys_message>();
            var curUserId = CurrentUser?.UserId;
            var curUserName = CurrentUser?.Username;
            var curUserCode = CurrentUser?.DisplayName;
            Task.Run(() =>
            {
                var check = _exportService.UserPermissionInfoById(model);
                if (!check.Success)
                {
                    result.AddError(check.AllMessages);
                    sys_message msgInfo = new sys_message() {
                        Message = $"對賬單刷新失敗!錯誤信息:{result.AllMessages}",
                        SendAt = DateTime.Now,
                        ReceiveUserId = curUserId,
                        ReceiveUserName = curUserName,
                        ReceiveUserCode = curUserCode,
                        IsRead = false,
                        InsertTime = DateTime.Now
                    };
                    messageList.Add(msgInfo);
                    MsgHelper.Current.Send(curUserName, Mapper.Map<MessageInfo>(msgInfo));
                }
                // 對賬單彙總
                var res = _reportService.InsertCustConfirmInfo(model);
                if (res.Success)
                {
                    // 最賬單明細彙總
                    var res_mx = _reportService.InsertCustConfirmDetail(model);
                    sys_message msgInfo = new sys_message()
                    {
                        Message = "對賬單刷新成功!",
                        SendAt = DateTime.Now,
                        ReceiveUserId = curUserId,
                        ReceiveUserName = curUserName,
                        ReceiveUserCode = curUserCode,
                        IsRead = false,
                        InsertTime = DateTime.Now
                    };
                    messageList.Add(msgInfo);
                    //發送對賬單彙總成功消息
                    MsgHelper.Current.Send(curUserName, Mapper.Map<MessageInfo>(msgInfo));
                }
                else
                {
                    sys_message msgInfo = new sys_message()
                    {
                        Message = $"對賬單刷新失敗!錯誤信息{res.AllMessages}",
                        SendAt = DateTime.Now,
                        ReceiveUserId = curUserId,
                        ReceiveUserName = curUserName,
                        ReceiveUserCode = curUserCode,
                        IsRead = false,
                        InsertTime = DateTime.Now
                    };
                    messageList.Add(msgInfo);
                    MsgHelper.Current.Send(curUserName, Mapper.Map<MessageInfo>(msgInfo));
                }
                message.MessageList = messageList;
                this.InsertSysMessage(message);
            });
        }

用戶消息管理方法,獲取和標記已讀供前臺調用

客戶端:

1、安裝nuget包:Microsoft.AspNet.SignalR.JS

2、配置相關js

 

<script src="~/Scripts/jquery.signalR-2.4.1.min.js"></script>
<script src="@System.Configuration.ConfigurationManager.AppSettings["ApiBaseUrl"]/signalrapi/hubs"></script>
<!-- 消息提示控件的js -->
<script src="~/Content/js/message/message.common.js"></script>
<script src="~/Content/js/message/message.directives.js"></script>

<script type="text/javascript">
    /*初始化SignalR*/
    //$(function () {
    //    // 申明一個Hub代理引用,首字母小寫
    //    var hub = $.connection.msgHub;
    //    // 服務端調用客戶端
    //    hub.client.serverToClient = function (message) {
    //        console.log("message:", message);
    //    }
    //    // signalr/hubs橋接
    //    $.connection.hub.url = apiBaseUrl + "signalrapi";
    //    // 啓用Hub事件的日誌記錄
    //    $.connection.hub.logging = true;
    //    // 啓動Hub連接,指定傳輸協議
    //    $.connection.hub.start({ transport: ['webSockets', 'longPolling'] }).done(function () {
    //        hub.server.clientToServer("zhaoqian");
    //    });
    //});

    /* Init Metronic's core jquery plugins and layout scripts */
    $(document).ready(function () {
        Metronic.init(); // Run metronic theme
        Metronic.setAssetsPath('/content/assets/'); // Set the assets folder path
    });
</script>

注:近期有很多朋友想要加qq跟我探討交流,因此我建立了一個技術交流溝通羣,另外羣內也會分享一些好的技術資源,大家感興趣的可以進羣,此項目的源碼在羣文件中也可找到(羣號碼:1055109975,可掃描下方二維碼進羣)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章