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

C# 重写Equals仅对比Id场景下List无法添加重复Allegato对象问题问询

问题根源

该问题由两个共同作用的原因导致:

  1. 未持久化的Allegato对象Id均为int类型默认值0,BaseObject中定义的Equals方法会判定所有未持久化的Allegato对象相等,配套的GetHashCode方法返回值也完全相同。
  2. NHibernate默认会将实体类中的ICollection类型属性按Set策略映射,运行时会将初始化的List实例替换为HashSet代理实现。HashSet添加元素时会自动调用Equals和GetHashCode做去重校验,因此只有第一个元素会被成功添加,后续被判定为相等的元素都会被直接丢弃。
解决方案

以下方案均不需要修改原有NHibernate基础设施逻辑:

方案1:重写Allegato的相等判定逻辑(最优)

BaseObject中的BaseObjectEquals和GetHashCode方法均为virtual修饰,可以在Allegato子类中重写,仅对未持久化的对象调整判定逻辑,持久化后仍沿用原有按Id比较的规则,完全兼容现有基础设施要求:

using System.Runtime.CompilerServices;

public class Allegato : BaseObject<Allegato, int>
{
    public override int Id { get; set; }
    public virtual string NomeFile { get; set; }

    public override bool BaseObjectEquals(BaseObject<Allegato, int> other)
    {
        if (other == null) return false;
        // 两个对象均未持久化时按引用比较
        if (Id == default && other.Id == default)
        {
            return ReferenceEquals(this, other);
        }
        // 已持久化对象沿用原有Id比较逻辑
        return base.BaseObjectEquals(other);
    }

    public override int GetHashCode()
    {
        // 未持久化时返回引用哈希码,已持久化时返回Id的哈希码
        return Id == default ? RuntimeHelpers.GetHashCode(this) : base.GetHashCode();
    }
}

该方案无需修改业务代码和映射配置,改动量最小,也不会影响现有NHibernate的运行逻辑。

方案2:调整集合映射为Bag类型

如果业务允许同一个Allegato对象多次关联到Corso,可以直接将集合的映射策略从默认的Set改为Bag,Bag对应运行时实现为List,不会做去重校验,自然不会触发相等性判定:

  • 若使用Fluent NHibernate映射,在Corso的映射配置中添加AsBag()声明即可:
HasMany(x => x.Allegati)
    .AsBag()
    .Cascade.AllDeleteOrphan(); // 沿用原有级联配置即可
  • 若使用hbm.xml映射,将集合的<set>标签替换为<bag>标签即可。

方案3:调整业务代码保存顺序

如果既不能修改实体定义也不能调整映射,可以调整业务代码的执行顺序,先保存父对象Corso,再逐个添加子对象并保存,确保每个Allegato添加到集合前已经拿到数据库生成的唯一Id:

using (myUow = myUowFactory.Create())
{
    var obj = new Corso();
    // 填充Corso的其他属性
    myUow.Corsi.Create(obj);
    myUow.SaveAll(); // 先保存父对象,此时关联集合为空

    var files = SessionManagement.LeggiOggetto<SessionObject.File>(HttpContext, SessionManagement.ChiaveSessione.File);
    foreach (var file in files)
    {
        var allegato = new Allegato { NomeFile = file.Nome };
        obj.AddAllegato(allegato);
        myUow.Allegati.Create(allegato); // 逐个保存子对象,生成唯一Id
    }

    myUow.SaveAll(); // 提交最终变更
}

内容的提问来源于stack exchange,提问作者gt.guybrush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 05:21:00