My problem is when saving a NHibernate entity here is the (pseudo)code:
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
}
}
Here is the entity and mapping:
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");
}
}
When commiting this I get the following exception :
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()
I'm running NHibernate 4.03 and running against Oracle 12.
This turned out to be as I expected a mapping issue but it was really hard to find.
The issue was with the related properties:
public virtual CnSubGroup CnSubGroupOut { get; set; }
public virtual CnSubGroup CnSubGroupIn { get; set; }
The CnSubGroup entity also has a version mapping and the error occurs when NHibernate is refreshing it's state and try to load these entities.
When I inspected the mapping of CnSubGroup I found the following:
References(x => x.MainCnSubGroup)
.Class<CnSubGroup>()
.Access.Property()
.Cascade.None()
.LazyLoad()
.Columns("MAINCNOTE", "MAINCNSUB", "PARTFACTORY");
NHibernate does not support insert or update of references with Composite keys, all those references should be mapped as readonly:
References(x => x.MainCnSubGroup)
.Class<CnSubGroup>()
.Access.Property()
.Cascade.None()
.LazyLoad()
.Not.Insert()
.Not.Update()
.Columns("MAINCNOTE", "MAINCNSUB", "PARTFACTORY");
With this code change the error went away.