保存 NHibernate 实体时获取 'Index was out of range. Must be non-negative and less than the size of the collection.'

Getting 'Index was out of range. Must be non-negative and less than the size of the collection.' when saving a NHibernate entity

我的问题是在此处保存 NHibernate 实体时是(伪)代码:

using (var session = OpenSession())
{
    using (var trans = session.BeginTransaction())
    {
        var reportTemplateVersion =
            new ReportTemplateVersion
            {
                ReportTemplateVersionOid = "0B89DB6-6CC1-460A-B913-10AFCF0C868    ",
                Description = "Testing",
                TextID_Description = "144bbdda-3249-4c51-8371-3c7ee551b75b  ",
                ChangeTimeStamp = DateTime.Now, 
                HasBeenUsed = false,
                PartFactory = "010",
                ObjVersion = 1,
                Version = 1,
                ReportTemplateOid = "93822BF-B275-44A5-9F29-AF65025C03C    ",
                CnSubIn = 1,
                CnoteIn = "15214",
                CnSubOut = null,
                CnoteOut = null
            };

        session.Save(reportTemplateVersion)

        trans.Commit(); <--- here I get the exception                   
    }
}

这是实体和映射:

public class ReportTemplateVersion
{
    public ReportTemplateVersion()
    {
        ReportCells = new HashSet<ReportCell>();
        ReportOrderRows = new HashSet<ReportOrderRow>();
    }

    public virtual string ReportTemplateVersionOid { get; set; }
    public virtual string ReportTemplateOid { get; set; }
    public virtual string Description { get; set; }
    public virtual string TextID_Description { get; set; }
    public virtual DateTime ChangeTimeStamp { get; set; }
    public virtual bool? HasBeenUsed { get; set; }
    public virtual string CnoteIn { get; set; }
    public virtual decimal CnSubIn { get; set; }
    public virtual string CnoteOut { get; set; }
    public virtual decimal? CnSubOut { get; set; }
    public virtual string PartFactory { get; set; }
    public virtual int ObjVersion { get; set; }
    public virtual decimal Version { get; set; }
    public virtual ReportTemplate ReportTemplate { get; set; }

    public virtual ISet<ReportCell> ReportCells { get; set; }
    public virtual ISet<ReportOrderRow> ReportOrderRows { get; set; }
    public virtual CnSubGroup CnSubGroupOut { get; set; }
    public virtual CnSubGroup CnSubGroupIn { get; set; }
}

public class ReportTemplateVersionMap : ClassMap<ReportTemplateVersion>
{
    public ReportTemplateVersionMap()
    {
        Table(@"REPORTTEMPLATEVERSION");
        LazyLoad();
        Id(x => x.ReportTemplateVersionOid)
          .Column("REPORTTEMPLATEVERSIONOID")
          .CustomType("String")
          .Access.Property()
          .CustomSqlType("CHAR")
          .Not.Nullable()
          .Length(38)
          .GeneratedBy.Assigned();
        Map(x => x.ReportTemplateOid)
          .Column("REPORTTEMPLATEOID")
          .CustomType("String")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("CHAR")
          .Length(38);
        Map(x => x.Description)
          .Column("DESCRIPTION")
          .CustomType("String")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("NVARCHAR2")
          .Length(255);
        Map(x => x.TextID_Description)
          .Column("TEXTID_DESCRIPTION")
          .CustomType("String")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("CHAR")
          .Length(38);
        Map(x => x.ChangeTimeStamp)
          .Column("CHANGETIMESTAMP")
          .CustomType("DateTime")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("DATE");
        Map(x => x.HasBeenUsed)
          .Column("HASBEENUSED")
          .CustomType("Boolean")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("NUMBER")
          .Precision(1);
        Map(x => x.CnoteIn)
          .Column("CNOTEIN")
          .CustomType("String")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("VARCHAR2")
          .Not.Nullable()
          .Length(10);
        Map(x => x.CnSubIn)
          .Column("CNSUBIN")
          .CustomType("Decimal")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("NUMBER")
          .Not.Nullable();
        Map(x => x.CnoteOut)
          .Column("CNOTEOUT")
          .CustomType("String")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("VARCHAR2")
          .Length(10);
        Map(x => x.CnSubOut)
          .Column("CNSUBOUT")
          .CustomType<decimal?>()
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("NUMBER");
        Map(x => x.PartFactory)
          .Column("PARTFACTORY")
          .CustomType("String")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("CHAR")
          .Not.Nullable()
          .Length(3);
        Version(x => x.ObjVersion)
          .Column("OBJVERSION")
          .CustomType("Int32")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("NUMBER")
          .Precision(38);
        Map(x => x.Version)
          .Column("VERSION")
          .CustomType("Decimal")
          .Access.Property()
          .Generated.Never()
          .CustomSqlType("NUMBER")
          .Not.Nullable()
          .Precision(38);
        References(x => x.ReportTemplate)
          .Class<ReportTemplate>()
          .Access.Property()
          .Cascade.None()
          .LazyLoad()
          .Not.Insert()
          .Not.Update()
          .Columns("REPORTTEMPLATEOID");
        HasMany(x => x.ReportCells)
          .Access.Property()
          .AsSet()
          //    There are no business rules stopping us from deleting a ReportCell when its ReportTemplateVersion 
          //    is deleted so we let nhibernate take care of it for us.
          .Cascade.Delete()
          .LazyLoad()
          .Inverse()
          .Generic()
          .KeyColumns.Add("REPORTTEMPLATEVERSIONOID", mapping => mapping.Name("REPORTTEMPLATEVERSIONOID")
                                                               .SqlType("CHAR")
                                                               .Nullable()
                                                               .Length(38));
        HasMany(x => x.ReportOrderRows)
          .Access.Property()
          .AsSet()
          .Cascade.None()
          .LazyLoad()
          .Inverse()
          .Generic()
          .KeyColumns.Add("REPORTTEMPLATEVERSIONOID", mapping => mapping.Name("REPORTTEMPLATEVERSIONOID")
                                                               .SqlType("CHAR")
                                                               .Nullable()
                                                               .Length(38));
        References(x => x.CnSubGroupOut)
          .Class<CnSubGroup>()
          .Access.Property()
          .Cascade.None()
          .Not.Insert()
          .Not.Update()
          .LazyLoad()
          .Columns("CNOTEOUT", "CNSUBOUT", "PARTFACTORY");
        References(x => x.CnSubGroupIn)
          .Class<CnSubGroup>()
          .Access.Property()
          .Cascade.None()
          .Not.Insert()
          .Not.Update()
          .LazyLoad()
          .Columns("CNOTEIN", "CNSUBIN", "PARTFACTORY");

    }
}

提交时出现以下异常:

Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index


Stack trace:
   at System.Collections.ArrayList.get_Item(Int32 index)
   at Oracle.ManagedDataAccess.Client.OracleParameterCollection.GetParameter(Int32 index)
   at System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item(Int32 index)
   at NHibernate.Type.Int32Type.Set(IDbCommand rs, Object value, Int32 index)
   at NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int32 index)
   at NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int32 index, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.UpdateOrInsert(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session)
   at NHibernate.Action.EntityUpdateAction.Execute()
   at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
   at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
   at NHibernate.Engine.ActionQueue.ExecuteActions()
   at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
   at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
   at NHibernate.Impl.SessionImpl.Flush()

我 运行 NHibernate 4.03 和 运行 反对 Oracle 12。

果然不出所料,是映射问题,但实在是不好找。

问题出在相关属性上:

public virtual CnSubGroup CnSubGroupOut { get; set; }
public virtual CnSubGroup CnSubGroupIn { get; set; }

CnSubGroup 实体也有一个版本映射,当 NHibernate 刷新它的状态并尝试加载这些实体时会发生错误。

当我检查 CnSubGroup 的映射时,我发现了以下内容:

    References(x => x.MainCnSubGroup)
        .Class<CnSubGroup>()
        .Access.Property()
        .Cascade.None()
        .LazyLoad()
        .Columns("MAINCNOTE", "MAINCNSUB", "PARTFACTORY");

NHibernate 不支持使用复合键插入或更新引用,所有这些引用都应映射为只读:

    References(x => x.MainCnSubGroup)
        .Class<CnSubGroup>()
        .Access.Property()
        .Cascade.None()
        .LazyLoad()
        .Not.Insert()
        .Not.Update()
        .Columns("MAINCNOTE", "MAINCNSUB", "PARTFACTORY");

通过更改此代码,错误消失了。

我有同样的错误信息,也是列映射问题。

我的情况是我重复定义了一个 属性,它已经在组件中定义了。

这个错误的根本原因是参数索引(我相信大多数情况下是Id的索引)超出了参数集合。

public class A()
{
   public virtual string aaa{get;set;}
   public virtual string bbb{get;set;} //bbb is defined in the BBB already!!!
   public virtual BBB bigB{get;set;}
}

public class BBB(){
   public virtual string bbb{get;set;}
}