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

Fluent NHibernate一对多映射报错:'Foreign Keys'集合名称无效

Fluent NHibernate映射异常排查:"Invalid collection name: 'Foreign Keys'"

问题重现

映射Asset和LogData类时,执行SchemaUpdate.Execute(false, true)抛出异常:

{"Invalid collection name: 'Foreign Keys'. (Parameter 'collectionName')"}

涉及的实体类和映射代码如下:

Asset实体类

public class Asset
{
    public virtual int AssetID { get; set; }
    public virtual string Name { get; set; } = "New Asset";
    public virtual double Value1 { get; set; }
    public virtual double Value2 { get; set; }
    public virtual double Value3 { get; set; }
    public virtual double Value4 { get; set; }
    public virtual double Value5 { get; set; }
    public virtual double Value6 { get; set; }
    public virtual double Value7 { get; set; }
    public virtual double Value8 { get; set; }
    [Range(0, 100)]
    public virtual double Value9 { get; set; }
    public virtual IEnumerable<LogData> Log { get; set; } = new List<LogData>();
}

LogData实体类

public class LogData
{
    public virtual Asset Parent { get; set; } = new();
    public virtual DateTime Timestamp { get; set; }
    public virtual double Value1 { get; set; }
    public virtual double Value2 { get; set; }
    public virtual double Value3 { get; set; }
    public override bool Equals(object? obj){...}
    public override int GetHashCode(){...}
}

Asset映射类

public AssetMap()
{
    Id(x => x.AssetID,"AssetID").GeneratedBy.Increment();
    Map(x => x.Name).Not.Nullable().Default("\'New Asset\'");
    Map(x => x.Value1).Not.Nullable();
    Map(x => x.Value3).Not.Nullable();
    Map(x => x.Value4).Not.Nullable();
    Map(x => x.Value5).Not.Nullable();
    Map(x => x.Value6).Not.Nullable();
    Map(x => x.Value7).Not.Nullable();
    Map(x => x.Value8).Not.Nullable();
    Map(x => x.Value9).Not.Nullable();
    HasMany(x => x.Log)
        .AsBag()
        .Cascade.AllDeleteOrphan()
        .KeyColumn("AssetID")
        .Not.LazyLoad();
}

LogData映射类

public LogDataMap()
{
    CompositeId()
        .KeyReference(x => x.Parent, "AssetID")
        .KeyProperty(x => x.Timestamp);
    Map(x => x.Value1).Not.Nullable();
    Map(x => x.Value2).Not.Nullable();
    Map(x => x.Value3).Not.Nullable();
}

解决方案

1. 升级NHibernate及Fluent NHibernate版本

这个异常在旧版本NHibernate(比如4.x及更早)中偶发,尤其是和SQL Server驱动配合时。建议升级到NHibernate 5.x+的稳定版本,同时匹配对应版本的Fluent NHibernate,很多Schema生成相关的bug在新版本中已修复。

2. 调整复合主键映射写法

复合主键中使用KeyReference可能导致Schema工具识别外键集合时出错,尝试显式映射关联字段,同时保留复合主键:

public LogDataMap()
{
    // 用AssetID作为复合主键的一部分,而非直接引用Parent
    CompositeId()
        .KeyProperty(x => x.Parent.AssetID, "AssetID")
        .KeyProperty(x => x.Timestamp);
    
    // 显式映射Parent关联,设置不插入/更新(因为主键已维护关联)
    References(x => x.Parent, "AssetID").Not.Insert().Not.Update();
    
    Map(x => x.Value1).Not.Nullable();
    Map(x => x.Value2).Not.Nullable();
    Map(x => x.Value3).Not.Nullable();
}

注意:确保LogData的Equals和GetHashCode方法基于Parent.AssetID和Timestamp实现,保证实体唯一性判断正确。

3. 改用独立主键+唯一约束

如果业务允许,给LogData添加独立主键,将AssetID和Timestamp设为唯一约束,彻底避免复合主键带来的问题:

修改LogData实体类

public class LogData
{
    public virtual int LogDataID { get; set; } // 新增独立主键
    public virtual Asset Parent { get; set; } = new();
    public virtual DateTime Timestamp { get; set; }
    public virtual double Value1 { get; set; }
    public virtual double Value2 { get; set; }
    public virtual double Value3 { get; set; }
    public override bool Equals(object? obj){...}
    public override int GetHashCode(){...}
}

修改LogData映射类

public LogDataMap()
{
    Id(x => x.LogDataID).GeneratedBy.Increment();
    References(x => x.Parent, "AssetID").Not.Nullable();
    Map(x => x.Timestamp).Not.Nullable();
    // 添加唯一约束,保证一个Asset在同一时间只有一条LogData
    UniqueKey("UQ_Asset_Timestamp").Columns("AssetID", "Timestamp");
    
    Map(x => x.Value1).Not.Nullable();
    Map(x => x.Value2).Not.Nullable();
    Map(x => x.Value3).Not.Nullable();
}

调整Asset映射类的HasMany配置

HasMany(x => x.Log)
    .AsBag()
    .Cascade.AllDeleteOrphan()
    .KeyColumn("AssetID")
    .Not.LazyLoad()
    .Inverse(); // 让LogData维护关联关系,避免冗余操作

4. 检查数据库驱动兼容性

确保使用的数据库驱动和NHibernate版本匹配,比如使用NHibernate.Driver.SqlClientDriver时,对应.NET版本的驱动包没有兼容性问题。

5. 直接使用SchemaExport初始化表结构

如果是首次初始化数据库,可以跳过SchemaValidator,直接用SchemaExport创建表:

new SchemaExport(configuration).Create(false, true);

创建完成后再用SchemaValidator验证结构即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 11:37:28