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.
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.