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

EF Core:WinForm场景下检测新增实体Add后的属性变更

解决方案

问题出在EF Core对新增实体的状态处理上:刚添加到上下文的实体处于Added状态,此时ChangeTracker.HasChanges()会直接返回true(因为实体是待插入的新记录),无法区分是用户修改了属性还是仅新增状态导致的标记。以下是两种可行的实现方式:

方案一:保存初始值快照,手动对比属性

在窗体加载时,复制一份实体的初始默认值作为快照,取消时对比当前实体与快照的属性差异:

  1. 在窗体类中定义私有字段存储初始快照:
private Visit _originalVisitSnapshot;
  1. 窗体加载时初始化实体并保存快照:
private void AddNewVisitForm_Load(object sender, EventArgs e)
{
    // 创建新实体并设置默认值
    var newVisit = new Visit();
    // 复制初始值作为快照(如果有复杂属性,需用深克隆,简单属性用MemberwiseClone或手动复制)
    _originalVisitSnapshot = (Visit)newVisit.MemberwiseClone();
    
    // 将实体添加到上下文并绑定到控件
    _dbContext.Add(newVisit);
    VisitBindingSource.DataSource = newVisit;
}
  1. 取消按钮点击时对比属性:
private void btnCancel_Click(object sender, EventArgs e)
{
    var currentVisit = (Visit)VisitBindingSource.DataSource;
    bool hasChanges = HasPropertiesChanged(_originalVisitSnapshot, currentVisit);

    if (hasChanges)
    {
        if (MessageBox.Show("您已修改内容,确定要取消吗?", "确认取消", MessageBoxButtons.YesNo) == DialogResult.No)
        {
            return;
        }
    }

    // 移除上下文里的新增实体,避免后续误保存
    _dbContext.Remove(currentVisit);
    this.Close();
}

// 通用属性对比方法(可复用)
private bool HasPropertiesChanged(Visit original, Visit current)
{
    foreach (var prop in typeof(Visit).GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        // 跳过导航属性(如果有),只对比值类型和字符串
        if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
            continue;

        var originalValue = prop.GetValue(original);
        var currentValue = prop.GetValue(current);

        if (!Equals(originalValue, currentValue))
        {
            return true;
        }
    }
    return false;
}

方案二:修改实体状态为Unchanged,利用ChangeTracker跟踪

通过AcceptAllChanges()将新增实体的状态改为Unchanged,让EF Core开始跟踪属性变更,这样就能正常使用ChangeDetector检测用户的修改:

  1. 窗体加载时初始化实体并调整状态:
private void AddNewVisitForm_Load(object sender, EventArgs e)
{
    var newVisit = new Visit();
    _dbContext.Add(newVisit);
    // 接受所有变更,将实体状态从Added改为Unchanged,此时EF会记录当前值为原始值
    _dbContext.ChangeTracker.AcceptAllChanges();
    
    VisitBindingSource.DataSource = newVisit;
}
  1. 取消按钮点击时检测变更:
private void btnCancel_Click(object sender, EventArgs e)
{
    var currentVisit = (Visit)VisitBindingSource.DataSource;
    var entityEntry = _dbContext.Entry(currentVisit);
    
    // 检测是否有属性变更
    bool hasChanges = entityEntry.HasChanges();

    if (hasChanges)
    {
        if (MessageBox.Show("您已修改内容,确定要取消吗?", "确认取消", MessageBoxButtons.YesNo) == DialogResult.No)
        {
            return;
        }
    }

    // 移除实体,关闭窗体
    _dbContext.Remove(currentVisit);
    this.Close();
}

注意:使用此方案时,若用户点击保存按钮,需要先将实体状态改回Added,否则EF不会插入这条记录:

private void btnSave_Click(object sender, EventArgs e)
{
    var currentVisit = (Visit)VisitBindingSource.DataSource;
    // 将状态改回Added,确保SaveChanges时插入数据
    _dbContext.Entry(currentVisit).State = EntityState.Added;
    _dbContext.SaveChanges();
    this.Close();
}

内容的提问来源于stack exchange,提问作者NAngelGr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 02:30:59