You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:44:57