I have a class called ProjectTaskBudget that has a property of type IBudgetableIncomeStream, like so:
public class ProjectTaskBudget
{
public virtual IBudgetableIncomeStream BudgetableIncomeStream { get; set; }
}
IBudgetableIncomeStream is an interface that’s applied to, among other things, an abstract class called Deliverable:
public abstract class Deliverable : IBudgetableIncomeStream
{
// Some stuff
}
and a concrete class called RecurringIncomeLine:
public class RecurringIncomeLine : IBudgetableIncomeStream
{
// Some stuff
}
The concrete classes that inherit from Deliverable are DeliverableItem and DeliverableTime.
The mapping for ProjectTaskBudget looks like this:
public class ProjectTaskBudgetMap : MappingBase<ProjectTaskBudget>
{
public ProjectTaskBudgetMap()
{
ReferencesAny(n => n.BudgetableIncomeStream)
.EntityTypeColumn("IncomeStreamType")
.EntityIdentifierColumn("IncomeStream_id")
.AddMetaValue<Deliverable>("Deliverable")
.AddMetaValue<RecurringIncomeLine>("RecurringIncomeLine")
.IdentityType<Guid>();
}
}
This works as it should with the RecurringIncomeLine, so I suspect the problem is to do with the abstract/interface mapping.
When I try to add a new ProjectTaskBudget to the database using a Deliverable, and flush, I get this error:
NHibernate.PropertyValueException: Error dehydrating property value for xxx.xxx.xxx.ProjectTaskBudget.BudgetableIncomeStream ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at NHibernate.Type.MetaType.NullSafeSet(IDbCommand st, Object value, Int32 index, ISessionImplementor session)
at NHibernate.Type.AnyType.NullSafeSet(IDbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index)
--- End of inner exception stack trace ---
at NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
at NHibernate.Action.EntityInsertAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at hiJump.Infrastructure.DataAccess.EventListeners.FixedDefaultFlushEventListener.PerformExecutions(IEventSource session) in c:\code\Git\Harmony\SharedCode\hiJump\hiJump.Infrastructure\DataAccess\EventListeners\FixedDefaultFlushEventListener.cs:line 35
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
I’m wondering if applying the interface directly to DeliverableItem and DeliverableTime would make a difference, and have the mapping include
.AddMetaValue<DeliverableItem>("DeliverableItem")
.AddMetaValue<DeliverableTime>("DeliverableTime")
but that’s a reasonably big refactor, so I wanted to check first and see if I was missing anything more obvious, or if that sounded like nonsense.
OK, I figured it out. The problem was in the mapping, but not what I thought.
The
IBudgetableIncomeStreamit was trying to add was in factsomething that derived from
DeliverableItem, so mappingDeliverableItemwasn’t enough, I have to put in every subclass ofDeliverableItemandDeliverableTimein the mapping, like: