0

I'm having a problem with nHibernate: If an entity inherits from a base entity, then this child entity cannot be referenced by another entity.


I have an inheritance hierarchy in my database as follows:

  • VideoFeed IS A VisualFeed
  • VideoFeed has many PlaylistAssignment

Here's the (paraphrased) SQL definition

create table VisualFeed
(
    id BIGINT NOT NULL  -- Primary key
)

create table VideoFeed
(
   id BIGINT NOT NULL   -- Primary key, foriegn key references VisualFeed
)

create table PlaylistAssignment
(
   id BIGINT NOT NULL,   -- Primary key
   VideoFeed_Id BIGINT NOT NULL   -- Foreign key to VideoFeed
)

And the class definitions

public class VisualFeed 
{
    public virtual long? Id { get; set; }
}

public class VideoFeed : VisualFeed
{
    public virtual ISet<PlaylistAssignment> PlaylistAssignments { get; set; }
}

public class PlaylistAssignment
{
    public virtual long? Id { get; set; }
    public virtual VideoFeed VideoFeed { get; set; }
}

Here's the mapping code for VisualFeed (the parent class):

public static void Map(ModelMapper mapper)
{
mapper.Class<DATMedia.CMS.EntityLibrary.Entities.VisualFeed>(
        classMapper =>
        {
            classMapper.Table("cms_VisualFeed");
            classMapper.Id(
                            visualFeed => visualFeed.Id,
                            idMapper =>
                            {
                                idMapper.Column("Id");
                                idMapper.Generator(Generators.HighLow,
                                                    generatorMapper =>
                                                    {
                                                        generatorMapper.Params(

                                                            new
                                                            {
                                                                max_lo = 256,
                                                                column = "NextHi",
                                                                where = "TableName='VisualFeed'"
                                                            }
                                                        );
                                                    }
                                );
                            }
            );
    });
}

Here's the mapping code for VideoFeed:

public static void Map(ModelMapper mapper)
{
    mapper.JoinedSubclass<DATMedia.CMS.EntityLibrary.Entities.VideoFeed>(
            joinedSubClassMapper =>
            {
                joinedSubClassMapper.Table("cms_VideoFeed");
                joinedSubClassMapper.Key(keyMapper =>
                    {
                        keyMapper.Column("Id");
                    }
                );


                joinedSubClassMapper.Set(
                    playerGroup => playerGroup.PlaylistAssignments,
                    setPropertiesMapper =>
                    {
                        setPropertiesMapper.Key(
                                keyMapper =>
                                {
                                    keyMapper.Column("VideoFeed_Id");
                                    keyMapper.PropertyRef(videoFeed => videoFeed.Id);
                                }
                            );
                        setPropertiesMapper.Cascade(Cascade.All | Cascade.DeleteOrphans);
                        setPropertiesMapper.Inverse(true);
                        setPropertiesMapper.OrderBy(playlistAssignment => playlistAssignment.AssignmentRank);
                    },
                    collectionElementRelation =>
                    {
                        collectionElementRelation.OneToMany();
                    }
                );    
            }
            );
}

And the mapping code for `PlaylistAssignment':

public static void Map(ModelMapper mapper)
{

    mapper.Class<DATMedia.CMS.EntityLibrary.Entities.PlaylistAssignment>(
            classMapper =>
            {
                classMapper.Table("cms_PlaylistAssignment");
                classMapper.Id(
                    playlistAssignment => playlistAssignment.Id,
                    idMapper =>
                    {
                        idMapper.Generator(Generators.Identity);
                    }

                    );

                classMapper.ManyToOne(
                        pa => pa.VideoFeed,
                        manyToOneMapper =>
                        {
                            manyToOneMapper.Column("VideoFeed_Id");
                            manyToOneMapper.Lazy(LazyRelation.Proxy);
                        }
                    );
           });
 };

An exception gets thrown when when calling ModelMapper.CompileMappingForAllExplicitlyAddedEntities.

The exception is thrown inside nHibernate code, in the file KeyMapper.cs:

public void PropertyRef(MemberInfo property)
{
   if (property == null)
   {
       mapping.propertyref = null;
       return;
   }
   if (!ownerEntityType.Equals(property.DeclaringType) && !ownerEntityType.Equals(property.ReflectedType))
   {
    throw new ArgumentOutOfRangeException("property", "Can't reference a property of another entity.");
   }
   mapping.propertyref = property.Name;
}

ownerEntityType equals "VideoFeed", and both ReflectedType and DeclaringType equals "VisualFeed" (the parent class name). Even though this is all correct, the ArgumentOutOfRangeException gets thrown.


Can anyone think of a workaround?


Later Edit This problem was caused by explicit reference calls to setPropertiesMapper. It was actually on a different child class which I had ommitted from the question (in a misguided attempt at simplifiying the problem).

The actual culprit is in the mapping to 'cms_VisualFeedAssignment' below:

        mapper.Class<DATMedia.CMS.EntityLibrary.Entities.VisualFeed>(
                classMapper =>
                {
                    classMapper.Table("cms_VisualFeed");
                    classMapper.Id(
                                    visualFeed => visualFeed.Id,
                                    idMapper =>
                                    {
                                        idMapper.Column("Id");
                                        idMapper.Generator(Generators.HighLow,
                                                            generatorMapper =>
                                                            {
                                                                generatorMapper.Params(

                                                                    new
                                                                    {
                                                                        max_lo = 256,
                                                                        column = "NextHi",
                                                                        where = "TableName='VisualFeed'"
                                                                    }
                                                                );
                                                            }
                                        );
                                    }
                    );
                 classMapper.Set<DATMedia.CMS.EntityLibrary.Entities.VisualFeedAssignment>(
                                    visualFeed => visualFeed.Assignments,
                                    bagPropertiesMapper =>
                                    {
                                        bagPropertiesMapper.Inverse(true);
                                        bagPropertiesMapper.Lazy(CollectionLazy.Lazy);
                                        bagPropertiesMapper.Key(
                                            keyMapper =>
                                            {
                                                keyMapper.Column("VisualFeed_Id");
                                                //keyMapper.PropertyRef<long?>(visualFeed => visualFeed.Id);
                                            }
                                        );
                                        bagPropertiesMapper.Table("cms_VisualFeedAssignment");
                                    },
                                    collectionElementRelation =>
                                    {
                                        collectionElementRelation.OneToMany();
                                    }
                                );
                        }
                    );
            }

When I commented out the PropertyRef call, it worked.

Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205

1 Answers1

1

You don't need this line:

keyMapper.PropertyRef(videoFeed => videoFeed.Id);

The VideoFeed.Id would automatically be referenced. Try removing that line, it should work.

Thilak Nathen
  • 1,333
  • 1
  • 8
  • 13
  • Thanks for the suggestion. The mapping now completes, but an exception gets thrown in a different part of the code. I've updated my question with the new error. – Andrew Shepherd Jan 07 '13 at 04:29
  • There's something else going on in your code. Could you provide the mapping for PlaylistAssignment? And what version of NH are you running? – Thilak Nathen Jan 07 '13 at 04:46
  • OK, I've added the PlaylistAssignment mapping. I'm using nHibernate 3.3.1 right now. I was going to upgrade to 3.3.2, but it seems you can only download the binaries, not the code package. (Do you know if the code has been packaged into a zip file on any site?) – Andrew Shepherd Jan 07 '13 at 05:00
  • NH 3.3.2 source is on http://sourceforge.net/projects/nhibernate/files/NHibernate/3.3.2GA/ – Thilak Nathen Jan 07 '13 at 20:54
  • I don't get any exceptions with your mapping on NH 3.3.1 and 3.3.2. I've posted a test project here: https://github.com/thilaknathen/StackOverflow-14133330. The basic structure that you've posted here works fine. There's something somewhere else that's failing. Try stripping your mappings down to the bare structure you've posted here and add the other mappings you might have on the objects incrementally till you hit the one that doesn't work. – Thilak Nathen Jan 07 '13 at 21:57
  • Thank you for doing this. I downloaded your example, and it worked for me too. It turned out there was another Set hanging off VisualFeedAssignment that also used the keyMapper.PropertyRef method. When I took that out, it all started working. (What is keyMapper.PropertyRef for, anyway). – Andrew Shepherd Jan 08 '13 at 02:23
  • PropertyRef is for associations where the join happens on a property that is the primary key. It's usually used for legacy databases where you can't change the db schema. – Thilak Nathen Jan 08 '13 at 02:32