Fluent Nhibernate HasMany

fluent-nhibernatenhibernate

I am having a gap in understanding and I would appreciate any help.

When I create a HasMany relationship using a list, nhibernate creates an "index" column on my child table. If I query the table using a join, it crashes with "Null Index on collection"

What I am not understanding is — how is this "index" column managed/populated?

My pertinent code:

Parent (Product)

  Class:
       public class License
       {
             ...
             public virtual IList<License>  Licenses { get; set; }
             public Product()
             {
                Licenses = (IList<License>)new List<License>();
             }
       }

   Mapping:

    HasMany<License>(x => x.Licenses)
        .Inverse()
        .Not.LazyLoad()
        .AsList()
        .Cascade.AllDeleteOrphan()
        .ForeignKeyCascadeOnDelete()
        .KeyNullable()
        .KeyColumn("Product_ID")

Child (License):

  Class:
       public class License
       {
             ...
             public virtual Product_ID {get; set;}
       }

  Mapping: 


    ...
    References<Product>(x => x.Product_ID)
        .Column("ParentProduct");
    ...

And my join:

   criteria.Add(Restrictions.Disjunction()
                        .Add(Restrictions.Like("Name", kw))
                        .Add(Restrictions.Like("Description", kw)))
                        .CreateAlias("Licenses", "License",   NHibernate.SqlCommand.JoinType.LeftOuterJoin);

I end up with:

[HibernateException: null index column for collection: Test.Licenses]
   NHibernate.Persister.Collection.AbstractCollectionPersister.ReadIndex(IDataReader rs, String[] aliases, ISessionImplementor session) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Persister\Collection\AbstractCollectionPersister.cs:703
   NHibernate.Collection.PersistentList.ReadFrom(IDataReader rs, ICollectionPersister role, ICollectionAliases descriptor, Object owner) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Collection\PersistentList.cs:120
   NHibernate.Loader.Loader.ReadCollectionElement(Object optionalOwner, Object optionalKey, ICollectionPersister persister, ICollectionAliases descriptor, IDataReader rs, ISessionImplementor session) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:706
   NHibernate.Loader.Loader.ReadCollectionElements(Object[] row, IDataReader resultSet, ISessionImplementor session) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:385
   NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:326
   NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:453
   NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:236
   NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:1649
   NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) in D:\source\elysium\Elysium.Web\FNH\src\NHibernate\Loader\Loader.cs:1568
...

EDIT

I changed the collection to Bag (or Set) and I get a different issue:

Line 30:        public void SetPropertyValues(object target, object[] values)
Line 31:        {
Line 32:            setDelegate(target, values, setterCallback);
Line 33:        }
Line 34: 


Source File: D:\source\Test\FNH\src\NHibernate\Bytecode\Lightweight\AccessOptimizer.cs    Line: 32

Stack Trace:

[InvalidCastException: Specified cast is not valid.]
   (Object , Object[] , SetterCallback ) +409
   NHibernate.Bytecode.Lightweight.AccessOptimizer.SetPropertyValues(Object target, Object[] values) in D:\source\Test\FNH\src\NHibernate\Bytecode\Lightweight\AccessOptimizer.cs:32
   NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object entity, Object[] values) in D:\source\Test\FNH\src\NHibernate\Tuple\Entity\PocoEntityTuplizer.cs:292

[PropertyAccessException: Invalid Cast (check your mapping for property type mismatches); setter of Test.License]
   NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object entity, Object[] values) in D:\source\Test\FNH\src\NHibernate\Tuple\Entity\PocoEntityTuplizer.cs:296


When I track it down,is seems that the problem is that my child class is:

public class License
{

public virtual Guid Id { get; private set; }
...
public virtual Guid Product_ID { get; set; }

}

But it is trying to put an instance of the Parent class in the Product_ID field. So it's actually trying to map Product_ID to an instance of Product class. So I end up with a Product that contains an array of Licenses, and it wants to have each License in the array contain an instance of the parent Product.

Best Solution

You can't use Inverse() together with an ordered collection (such as a list) - if you want to make it ordered then you need to omit the Inverse() and persist it from the parent item

Related Question