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