ASP. NET Signal是爲ASP.NET開發人員提供的一個庫,可以簡化開發人員將實時Web功能添加到應用程序的過程。實時Web功能是指這樣一種功能:當所連接的客戶端變得可用時服務器代碼可以立即向其推送內容,而不是讓服務器等待客戶端請求新的數據。
我個人的理解是:當客戶端向服務器端發起一次請求,建立連接之後,當服務器端數據發生了變化,無需客戶端再次發起請求,服務器端即可將更新後的數據主動推送給客戶端。實際工作中可以用它做很多事情,如果用戶是通過刷新web頁面,來查看新的數據,或者是通過頁面實現長輪詢來檢索新的數據,那麼就該考慮使Signal了。示例包括儀表板和監視應用程序、協作應用程序(例如同時編輯文檔)、工作進度更新和實時表單等等。
Signal會自動管理連接,並允許您像Chat室那樣向所有連接的客戶端同時發送消息。你也可以向特定的客戶端發送消息。客戶端和服務器之間的連接是持久性的,不像傳統的HITP連接每個通信都需要重新建立一個連接Signal支持“服務器推送”功能,即服務器代碼可以使用遠程過程調用(PRC)來調用瀏覽器中的客戶端代碼,而不使用目前在Web上常用的請求ー響應模型Signal應用程序可以通過使用服務總線。SQL Server或 Redis擴展到數以千計的客戶端Signal是開源的,可以通過 Github訪問。
由於VFS系統功能邏輯較爲複雜,某些功能操作後等待時間較長,可以運用SignalR來減少使用者在等待返回結果上的時間浪費。例如:對賬單刷新功能,此功能需要刷新報表中的相關結算單位的當月的對賬單明細、對賬單彙總的數據,由於報表的邏輯較爲複雜,因此如果結算單位單量很大的情況下刷新的過程比較長,因此引入SignalR,當客戶端運行時會首先與服務器建立連接,發起對賬單刷新請求時,會異步執行數據的刷新操作,異步執行的過程中會直接返回“系統正在刷新數據”的通知消息,此時,使用者不必在此等候,可先去做其他工作,等數據刷新執行完畢後,服務器會主動向客戶端發起消息通知,通知客戶端對賬單刷新已完成。
Demo下載地址(可點擊此處下載)
服務器端:
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
<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,可掃描下方二維碼進羣)