基于.NET Framework 4.8+WebForms+ASP.NET Identity实现站内私信功能
基于.NET Framework 4.8 + WebForms + ASP.NET Identity的本地私信实现方案
一、核心数据模型设计
首先通过EF6(适配.NET Framework 4.8)设计私信实体,关联ASP.NET Identity的用户体系:
1. 私信实体与状态枚举
public class PrivateMessage { public int Id { get; set; } public string SenderId { get; set; } // 发送者ID,关联ApplicationUser public string RecipientId { get; set; } // 接收者ID public string Subject { get; set; } public string Content { get; set; } public DateTime? SentDate { get; set; } // 草稿状态下为null public DateTime? ReadDate { get; set; } public MessageStatus Status { get; set; } // 导航属性 public virtual ApplicationUser Sender { get; set; } public virtual ApplicationUser Recipient { get; set; } } public enum MessageStatus { Draft, // 草稿 Sent, // 已发送未读 Read, // 已读 DeletedBySender, // 发送者标记删除 DeletedByRecipient // 接收者标记删除 }
2. 更新DbContext
在ApplicationDbContext中添加数据集并配置外键:
public DbSet<PrivateMessage> PrivateMessages { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 配置用户与私信的关联,避免级联删除 modelBuilder.Entity<PrivateMessage>() .HasRequired(m => m.Sender) .WithMany() .HasForeignKey(m => m.SenderId) .WillCascadeOnDelete(false); modelBuilder.Entity<PrivateMessage>() .HasRequired(m => m.Recipient) .WithMany() .HasForeignKey(m => m.RecipientId) .WillCascadeOnDelete(false); }
执行Add-Migration和Update-Database命令生成数据库表。
二、封装业务逻辑
创建MessageService类封装私信核心操作,避免页面后台代码冗余:
public class MessageService { private readonly ApplicationDbContext _context; private readonly UserManager<ApplicationUser> _userManager; public MessageService(ApplicationDbContext context, UserManager<ApplicationUser> userManager) { _context = context; _userManager = userManager; } // 保存/更新草稿 public void SaveDraft(string senderId, string recipientId, string subject, string content, int? draftId = null) { PrivateMessage draft; if (draftId.HasValue) { draft = _context.PrivateMessages.Find(draftId.Value); if (draft == null || draft.SenderId != senderId || draft.Status != MessageStatus.Draft) throw new InvalidOperationException("无效的草稿"); } else { draft = new PrivateMessage { SenderId = senderId, Status = MessageStatus.Draft }; _context.PrivateMessages.Add(draft); } draft.RecipientId = recipientId; draft.Subject = subject; draft.Content = content; _context.SaveChanges(); } // 发送消息(支持从草稿转换) public void SendMessage(string senderId, string recipientId, string subject, string content, int? draftId = null) { PrivateMessage message; if (draftId.HasValue) { message = _context.PrivateMessages.Find(draftId.Value); if (message == null || message.SenderId != senderId || message.Status != MessageStatus.Draft) throw new InvalidOperationException("无效的草稿"); _context.PrivateMessages.Remove(message); } message = new PrivateMessage { SenderId = senderId, RecipientId = recipientId, Subject = subject, Content = content, Status = MessageStatus.Sent, SentDate = DateTime.Now }; _context.PrivateMessages.Add(message); _context.SaveChanges(); } // 获取收件箱(排除接收者已删除的消息) public List<PrivateMessage> GetInbox(string userId) { return _context.PrivateMessages .Include(m => m.Sender) .Where(m => m.RecipientId == userId && m.Status != MessageStatus.DeletedByRecipient) .OrderByDescending(m => m.SentDate) .ToList(); } // 获取草稿箱 public List<PrivateMessage> GetDrafts(string userId) { return _context.PrivateMessages .Include(m => m.Recipient) .Where(m => m.SenderId == userId && m.Status == MessageStatus.Draft) .OrderByDescending(m => m.Id) .ToList(); } // 标记消息为已读 public void MarkAsRead(int messageId, string userId) { var message = _context.PrivateMessages.Find(messageId); if (message != null && message.RecipientId == userId && message.Status == MessageStatus.Sent) { message.Status = MessageStatus.Read; message.ReadDate = DateTime.Now; _context.SaveChanges(); } } // 软删除消息(双方都删除则物理删除) public void DeleteMessage(int messageId, string userId) { var message = _context.PrivateMessages.Find(messageId); if (message == null) return; if (message.SenderId == userId) { if (message.Status == MessageStatus.DeletedByRecipient) _context.PrivateMessages.Remove(message); else message.Status = MessageStatus.DeletedBySender; } else if (message.RecipientId == userId) { if (message.Status == MessageStatus.DeletedBySender) _context.PrivateMessages.Remove(message); else message.Status = MessageStatus.DeletedByRecipient; } _context.SaveChanges(); } }
三、WebForms页面实现示例
1. 新建/编辑消息页(SendMessage.aspx)
- 后台核心代码:
public partial class SendMessage : System.Web.UI.Page { private MessageService _messageService; private ApplicationUser _currentUser; protected void Page_Load(object sender, EventArgs e) { if (!User.Identity.IsAuthenticated) { Response.Redirect("~/Account/Login.aspx"); return; } var context = new ApplicationDbContext(); var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)); _messageService = new MessageService(context, userManager); _currentUser = userManager.FindById(User.Identity.GetUserId()); // 加载草稿内容(如果是编辑草稿) if (!IsPostBack && Request.QueryString["DraftId"] != null) { int draftId = int.Parse(Request.QueryString["DraftId"]); var draft = _messageService.GetDrafts(_currentUser.Id).FirstOrDefault(m => m.Id == draftId); if (draft != null) { txtRecipient.Text = draft.Recipient.UserName; txtSubject.Text = draft.Subject; txtContent.Text = draft.Content; hidDraftId.Value = draftId.ToString(); } } } protected void btnSaveDraft_Click(object sender, EventArgs e) { var recipient = UserManager.FindByName(txtRecipient.Text); if (recipient == null) { lblError.Text = "指定用户不存在"; return; } int? draftId = string.IsNullOrEmpty(hidDraftId.Value) ? null : (int?)int.Parse(hidDraftId.Value); _messageService.SaveDraft(_currentUser.Id, recipient.Id, txtSubject.Text, txtContent.Text, draftId); Response.Redirect("Drafts.aspx"); } protected void btnSend_Click(object sender, EventArgs e) { var recipient = UserManager.FindByName(txtRecipient.Text); if (recipient == null) { lblError.Text = "指定用户不存在"; return; } int? draftId = string.IsNullOrEmpty(hidDraftId.Value) ? null : (int?)int.Parse(hidDraftId.Value); _messageService.SendMessage(_currentUser.Id, recipient.Id, txtSubject.Text, txtContent.Text, draftId); Response.Redirect("Inbox.aspx"); } }
- 前台添加收件人输入框、标题输入框、内容文本域,以及「保存草稿」「发送」按钮。
2. 收件箱页(Inbox.aspx)
- 后台核心代码:
public partial class Inbox : System.Web.UI.Page { private MessageService _messageService; private ApplicationUser _currentUser; protected void Page_Load(object sender, EventArgs e) { if (!User.Identity.IsAuthenticated) { Response.Redirect("~/Account/Login.aspx"); return; } var context = new ApplicationDbContext(); var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)); _messageService = new MessageService(context, userManager); _currentUser = userManager.FindById(User.Identity.GetUserId()); if (!IsPostBack) { BindInboxMessages(); } } private void BindInboxMessages() { var inbox = _messageService.GetInbox(_currentUser.Id); lvInbox.DataSource = inbox; lvInbox.DataBind(); } protected void lvInbox_ItemCommand(object sender, ListViewCommandEventArgs e) { int messageId = int.Parse(e.CommandArgument.ToString()); if (e.CommandName == "MarkAsRead") { _messageService.MarkAsRead(messageId, _currentUser.Id); } else if (e.CommandName == "Delete") { _messageService.DeleteMessage(messageId, _currentUser.Id); } BindInboxMessages(); } }
- 前台用
ListView控件展示消息列表,显示发送者、标题、发送时间,添加「标记已读」「删除」按钮。
3. 草稿箱页(Drafts.aspx)
逻辑类似收件箱,绑定草稿列表,提供「编辑」「删除」「发送」按钮,调用MessageService对应的方法即可。
四、关键注意事项
- 权限控制:所有私信页面需验证用户登录状态,未登录则跳转登录页。
- XSS防护:对消息内容使用
Server.HtmlEncode处理,前端输入框添加XSS过滤逻辑。 - 性能优化:消息量大时添加分页查询,使用
Include预加载关联用户避免N+1查询。 - 异常处理:在业务逻辑层和页面后台添加try-catch块,捕获数据库操作异常并给出友好提示。
内容的提问来源于stack exchange,提问作者Mardibak
相关产品推荐
相关产品推荐

