本文介绍了ASP.NET MVC4异步聊天室的示例代码,分享给大家,具体如下:
类图:
Domain层
IChatRoom.cs
using System;
using System.Collections.Generic;
namespace MvcAsyncChat.Domain
{
public interface IChatRoom
{
void AddMessage(string message);
void AddParticipant(string name);
void GetMessages(
DateTime since,
Action<IEnumerable<string>, DateTime> callback);
void RemoveParticipant(string name);
}
}
IMessageRepo.cs
using System;
using System.Collections.Generic;
namespace MvcAsyncChat.Domain
{
public interface IMessageRepo
{
DateTime Add(string message);
IEnumerable<string> GetSince(DateTime since);
}
}
ICallbackQueue.cs
using System;
using System.Collections.Generic;
namespace MvcAsyncChat.Domain
{
public interface ICallbackQueue
{
void Enqueue(Action<IEnumerable<string>, DateTime> callback);
IEnumerable<Action<IEnumerable<string>, DateTime DequeueAll();
IEnumerable<Action<IEnumerable<string>, DateTime DequeueExpired(DateTime expiry);
}
}
ChatRoom.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MvcAsyncChat.Svcs;
namespace MvcAsyncChat.Domain
{
public class ChatRoom : IChatRoom
{
readonly ICallbackQueue callbackQueue;
readonly IDateTimeSvc dateTimeSvc;
readonly IMessageRepo messageRepo;
public ChatRoom(
ICallbackQueue callbackQueue,
IDateTimeSvc dateTimeSvc,
IMessageRepo messageRepo)
{
this.callbackQueue = callbackQueue;
this.dateTimeSvc = dateTimeSvc;
this.messageRepo = messageRepo;
}
public void AddMessage(string message)
{
var timestamp = messageRepo.Add(message);
foreach (var callback in callbackQueue.DequeueAll())
callback(new[] { message }, timestamp);
}
public void AddParticipant(string name)
{
AddMessage(string.Format("{0} 已进入房间.", name));
}
public void GetMessages(
DateTime since,
Action<IEnumerable<string>, DateTime> callback)
{
var messages = messageRepo.GetSince(since);
if (messages.Count() > 0)
callback(messages, since);
else
callbackQueue.Enqueue(callback);
}
public void RemoveParticipant(string name)
{
AddMessage(string.Format("{0} left the room.", name));
}
}
}
InMemMessageRepo.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace MvcAsyncChat.Domain
{
public class InMemMessageRepo : IMessageRepo
{
public InMemMessageRepo()
{
Messages = new List<Tuple<string, DateTime();
}
public IList<Tuple<string, DateTime Messages { get; private set; }
public DateTime Add(string message)
{
var timestamp = DateTime.UtcNow;
Messages.Add(new Tuple<string, DateTime>(message, timestamp));
return timestamp;
}
public IEnumerable<string> GetSince(DateTime since)
{
return Messages
.Where(x => x.Item2 > since)
.Select(x => x.Item1);
}
}
}
CallbackQueue.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace MvcAsyncChat.Domain
{
public class CallbackQueue : ICallbackQueue
{
public CallbackQueue()
{
Callbacks = new Queue<Tuple<Action<IEnumerable<string>, DateTime>, DateTime();
}
public Queue<Tuple<Action<IEnumerable<string>, DateTime>, DateTime Callbacks { get; private set; }
public void Enqueue(Action<IEnumerable<string>, DateTime> callback)
{
Callbacks.Enqueue(new Tuple<Action<IEnumerable<string>, DateTime>, DateTime>(callback, DateTime.UtcNow));
}
public IEnumerable<Action<IEnumerable<string>, DateTime DequeueAll()
{
while (Callbacks.Count > 0)
yield return Callbacks.Dequeue().Item1;
}
public IEnumerable<Action<IEnumerable<string>, DateTime DequeueExpired(DateTime expiry)
{
if (Callbacks.Count == 0)
yield break;
var oldest = Callbacks.Peek();
while (Callbacks.Count > 0 && oldest.Item2 <= expiry)
{
yield return Callbacks.Dequeue().Item1;
if (Callbacks.Count > 0)
oldest = Callbacks.Peek();
}
}
}
}
RequestModels文件夹实体类
EnterRequest.cs
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MvcAsyncChat.RequestModels
{
public class EnterRequest
{
[DisplayName("名称")]
[Required, StringLength(16), RegularExpression(@"^[A-Za-z0-9_\ -]+$", ErrorMessage="A name must be alpha-numeric.")]
public string Name { get; set; }
}
}
GetMessagesRequest.cs
using System;
namespace MvcAsyncChat.RequestModels
{
public class GetMessagesRequest
{
public string since { get; set; }
}
}
SayRequest.cs
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MvcAsyncChat.RequestModels
{
public class SayRequest
{
[Required, StringLength(1024), DataType(DataType.MultilineText)]
public string Text { get; set; }
}
}
ResponseModels文件夹实体类
GetMessagesResponse.cs
using System;
using System.Collections.Generic;
namespace MvcAsyncChat.ResponseModels
{
public class GetMessagesResponse
{
public string error { get; set; }
public IEnumerable<string> messages { get; set; }
public string since { get; set; }
}
}
SayResponse.cs
using System;
namespace MvcAsyncChat.ResponseModels
{
public class SayResponse
{
public string error { get; set; }
}
}
ChatController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Async;
using MvcAsyncChat.Domain;
using MvcAsyncChat.RequestModels;
using MvcAsyncChat.ResponseModels;
using MvcAsyncChat.Svcs;
namespace MvcAsyncChat.Controllers
{
public class ChatController : AsyncController
{
readonly IAuthSvc authSvc;
readonly IChatRoom chatRoom;
readonly IDateTimeSvc dateTimeSvc;
public ChatController(
IAuthSvc authSvc,
IChatRoom chatRoom,
IDateTimeSvc dateTimeSvc)
{
this.authSvc = authSvc;
this.chatRoom = chatRoom;
this.dateTimeSvc = dateTimeSvc;
}
[ActionName("enter"), HttpGet]
public ActionResult ShowEnterForm()
{
if (User.Identity.IsAuthenticated)
return RedirectToRoute(RouteName.Room);
return View();
}
[ActionName("enter"), HttpPost]
public ActionResult EnterRoom(EnterRequest enterRequest)
{
if (!ModelState.IsValid)
return View(enterRequest);
authSvc.Authenticate(enterRequest.Name);
chatRoom.AddParticipant(enterRequest.Name);
return RedirectToRoute(RouteName.Room);
}
[ActionName("room"), HttpGet, Authorize]
public ActionResult ShowRoom()
{
return View();
}
[ActionName("leave"), HttpGet, Authorize]
public ActionResult LeaveRoom()
{
authSvc.Unauthenticate();
chatRoom.RemoveParticipant(User.Identity.Name);
return RedirectToRoute(RouteName.Enter);
}
[HttpPost, Authorize]
public ActionResult Say(SayRequest sayRequest)
{
if (!ModelState.IsValid)
return Json(new SayResponse() { error = "该请求无效." });
chatRoom.AddMessage(User.Identity.Name+" 说:"+sayRequest.Text);
return Json(new SayResponse());
}
[ActionName("messages"), HttpPost, Authorize]
public void GetMessagesAsync(GetMessagesRequest getMessagesRequest)
{
AsyncManager.OutstandingOperations.Increment();
if (!ModelState.IsValid)
{
AsyncManager.Parameters["error"] = "The messages request was invalid.";
AsyncManager.Parameters["since"] = null;
AsyncManager.Parameters["messages"] = null;
AsyncManager.OutstandingOperations.Decrement();
return;
}
var since = dateTimeSvc.GetCurrentDateTimeAsUtc();
if (!string.IsNullOrEmpty(getMessagesRequest.since))
since = DateTime.Parse(getMessagesRequest.since).ToUniversalTime();
chatRoom.GetMessages(since, (newMessages, timestamp) =>
{
AsyncManager.Parameters["error"] = null;
AsyncManager.Parameters["since"] = timestamp;
AsyncManager.Parameters["messages"] = newMessages;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult GetMessagesCompleted(
string error,
DateTime"o");
data.messages = messages;
return Json(data);
}
}
}
room.js
var since = "",
errorCount = 0,
MAX_ERRORS = 6;
function addMessage(message, type) {
$("#messagesSection > td").append("<div class='" + (type || "") + "'>" + message + "</div>")
}
function showError(error) {
addMessage(error.toString(), "error");
}
function onSayFailed(XMLHttpRequest, textStatus, errorThrown) {
showError("An unanticipated error occured during the say request: " + textStatus + "; " + errorThrown);
}
function onSay(data) {
if (data.error) {
showError("An error occurred while trying to say your message: " + data.error);
return;
}
}
function setSayHandler() {
$("#Text").keypress(function (e) {
if (e.keyCode == 13) {
$("#sayForm").submit();
$("#Text").val("");
return false;
}
});
}
function retryGetMessages() {
if (++errorCount > MAX_ERRORS) {
showError("There have been too many errors. Please leave the chat room and re-enter.");
}
else {
setTimeout(function () {
getMessages();
}, Math.pow(2, errorCount) * 1000);
}
}
function onMessagesFailed(XMLHttpRequest, textStatus, errorThrown) {
showError("An unanticipated error occured during the messages request: " + textStatus + "; " + errorThrown);
retryGetMessages();
}
function onMessages(data, textStatus, XMLHttpRequest) {
if (data.error) {
showError("An error occurred while trying to get messages: " + data.error);
retryGetMessages();
return;
}
errorCount = 0;
since = data.since;
for (var n = 0; n < data.messages.length; n++)
addMessage(data.messages[n]);
setTimeout(function () {
getMessages();
}, 0);
}
function getMessages() {
$.ajax({
cache: false,
type: "POST",
dataType: "json",
url: "/messages",
data: { since: since },
error: onMessagesFailed,
success: onMessages,
timeout: 100000
});
}
Chat视图文件夹
Enter.cshtml
@model MvcAsyncChat.RequestModels.EnterRequest
@{
View.Title = "Enter";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@section Head {}
<tr id="enterSection">
<td>
<h2>[MVC聊天]是使用ASP.NET MVC 3的异步聊天室
<table>
<tr>
<td class="form-container">
<fieldset>
<legend>进入聊天室</legend>
@using(Html.BeginForm()) {
@Html.EditorForModel()
<input type="submit" value="Enter" />
}
</fieldset>
</td>
</tr>
</table>
</td>
</tr>
@section PostScript {
<script>
$(document).ready(function() {
$("#Name").focus();
});
</script>
}
Room.cshtml
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。


