Search code examples
c#fluent-nhibernatemappingdiscriminator

FluentNHibernate: mapping child collection with discriminator


Viewing my object with nested collection don't throw an error, but when trying to save the entity i had an error

    [IndexOutOfRangeException: Invalid index 15 for SqlParameterCollection with Count=15.]
   System.Data.SqlClient.SqlParameterCollection.RangeCheck(Int32 index) +5343807
   System.Data.SqlClient.SqlParameterCollection.GetParameter(Int32 index) +19
   System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item(Int32 index) +10
   NHibernate.Type.Int64Type.Set(IDbCommand rs, Object value, Int32 index) +60
   NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int32 index) +414
   NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session) +62
   NHibernate.Type.ManyToOneType.NullSafeSet(IDbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session) +122
   NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index) +344

[PropertyValueException: Error dehydrating property value for PravUprav.Classes.Entities.ClaimCaseInstanceFI.Decision]
   NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index) +503
   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) +2013
   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) +335
   NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session) +1898
   NHibernate.Action.EntityUpdateAction.Execute() +737
   NHibernate.Engine.ActionQueue.Execute(IExecutable executable) +39
   NHibernate.Engine.ActionQueue.ExecuteActions(IList list) +128
   NHibernate.Engine.ActionQueue.ExecuteActions() +48
   NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) +241
   NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) +179
   NHibernate.Impl.SessionImpl.Flush() +295
   NHibernate.Transaction.AdoTransaction.Commit() +189
   Spring.Data.NHibernate.HibernateTransactionManager.DoCommit(DefaultTransactionStatus status) in c:\_prj\spring-net\src\Spring\Spring.Data.NHibernate\Data\NHibernate\HibernateTransactionManager.cs:561

[DataIntegrityViolationException: Error dehydrating property value for PravUprav.Classes.Entities.ClaimCaseInstanceFI.Decision]
   Spring.Data.NHibernate.HibernateTransactionManager.DoCommit(DefaultTransactionStatus status) in c:\_prj\spring-net\src\Spring\Spring.Data.NHibernate\Data\NHibernate\HibernateTransactionManager.cs:577

Error:

Error dehydrating property value for 

says that I mapped property twice or DataTypes of entities properties incorrect, but i checked and not found error in my code.

Entity with collection:

public class ClaimCase
{
    private IList<ClaimCaseInstance> _claimCaseInstances;
    public virtual IList<ClaimCaseInstance> ClaimCaseInstances
    {
       get { return _claimCaseInstances ?? (_claimCaseInstances = new List<ClaimCaseInstance>()); }
          set { _claimCaseInstances = value; }
     }
}

Mapping for entity:

public sealed class ClaimCaseMap : ClassMap<ClaimCase>
{
   public ClaimCaseMap()
      {
         Table("ClaimCase");
         Id(x => x.Id);                
         HasMany(x => x.ClaimCaseInstances).Inverse().Cascade.AllDeleteOrphan();
       }
 }

ClaimCaseInstance entity:

public abstract class ClaimCaseInstance
{
    public virtual ClaimCase ClaimCase { get; set; }
    public virtual DicList Result { get; set; }
}

Child classes:

public class ClaimCaseInstanceFI : ClaimCaseInstance
{
    public virtual DicList Decision { get; set; }
}

public class ClaimCaseInstanceAI : ClaimCaseInstance
{
}

Mappings:

public sealed class ClaimCaseInstanceMap : ClassMap<ClaimCaseInstance>
{
    public ClaimCaseInstanceMap()
    {
        Table("ClaimCaseInstances");
        Id(x => x.Id);            
        DiscriminateSubClassesOnColumn("InstanceType");

        References(x => x.ClaimCase);
        References(x => x.Result);
    }
}

public sealed class ClaimCaseInstanceFIMap : SubclassMap<ClaimCaseInstanceFI>
{
    public ClaimCaseInstanceFIMap()
    {
        DiscriminatorValue((int)CourtTypes.Firts);
        References(x => x.Decision);
    }
}

public sealed class ClaimCaseInstanceAIMap : SubclassMap<ClaimCaseInstanceAI>
{
    public ClaimCaseInstanceAIMap()
    {
        DiscriminatorValue((int)CourtTypes.Appeals);
    }
}

CourtTypes:

public enum CourtTypes
{
    First = 5,
    Appeals = 6
}

DicList reference mapping by convention ForeignKeyConversion as instance.Property.Name + "ID" and it's work fine.

Error only occurs when saving ClaimCase after editing a field Decision


Solution

  • I've identified the source of the problem. Using the great tool dotPeek (thanks @SergSW for the tip) as symbol server and debug

    NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate()
    

    i found that count of Updated fields in sql statement are not equal count

    entityMetamodel.PropertySpan
    

    Some of the fields in the array entityMetamodel.Properties were present twice.

    public sealed class ClaimCaseInstanceMap : ClassMap<ClaimCaseInstance>
        {
            public ClaimCaseInstanceMap()
            {
                Table("ClaimCaseInstances");
    
                DiscriminateSubClassesOnColumn("InstanceType");
    
                Id(x => x.Id);  
                References(x => x.ClaimCase);
                Map(x => x.FilingDate);
                References(x => x.Result);
                Map(x => x.PriceResult);
                Map(x => x.ExpenseResult);
                Map(x => x.Comment);
            }
        }
    

    SubClass:

    public sealed class ClaimCaseInstanceFIMap : SubclassMap<ClaimCaseInstanceFI>
    {
        public ClaimCaseInstanceFIMap()
        {
            DiscriminatorValue((int)CourtType.Firts);
    
            Id(x => x.Id);  
            Map(x => x.Price);
            Map(x => x.Expense);
            References(x => x.Decision);
            References(x => x.Determination);
            Map(x => x.DeterminationDate);
            Map(x => x.CourtDateAct);
            Map(x => x.PriceResult);
            Map(x => x.ExpenseResult);
            Map(x => x.CounterClaimDate);
            Map(x => x.Comment);
        }
    }
    

    As you can see, this properties: Comment, ExpenseResult, PriceResult.

    I've not described the mapping for all the fields in my question, it's my error, sorry for that and thank you for your help.