Fluent Nhibernate HasMany

14,381

Solution 1

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

Solution 2

Try mapping it as an unordered collection (bag):

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

Edited to add:

You shouldn't define the foreign key on the child object in addition to defining the many-to-one object through References. So License should be:

   public class License
   {
         ...
         public virtual Product {get; set;}
   }

Also, I assume you meant Product here and the cast is unnecessary because List<T> implements IList<T>:

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

Hope this helps.

Share:
14,381
Will I Am
Author by

Will I Am

Not Available

Updated on June 04, 2022

Comments

  • Will I Am
    Will I Am almost 2 years

    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.

  • Will I Am
    Will I Am almost 14 years
    Thank you. That does work, however I would still like to understand how to handle the Index column in case I do want "AsList". I've seen plenty of examples using AsList() so I'm assuming it's not "disallowed".
  • Will I Am
    Will I Am almost 14 years
    Actually it doesn't work that as a Bag either (maybe a different issue?). I am getting a cast error. I updated the original post. I compiled nHibernate from source to get more information about the crash. It looks like its' trying to create an array of children products and create the Product_ID column as an instance of the parent class.
  • Jamie Ide
    Jamie Ide almost 14 years
    AsList maps the collection as an ordered collection and you have to have a database column to store the index. This is a good overview: blogs.hibernatingrhinos.com/nhibernate/archive/2008/06/12/…
  • Will I Am
    Will I Am almost 14 years
    Thanks Jamie, that does fix the problem with the AsBag(). AsList() I still have the null index error. But I will mark your answer as accepted. I hope someone will explain to me the AsList issue!
  • Will I Am
    Will I Am almost 14 years
    Thanks, i will read the post.. I think we crossed wires while posting.