awatson
unread,Nov 6, 2008, 9:24:03 AM11/6/08Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to nhusers
The objective here is to have a class Widget that contains a single
instance of WidgetCategory. Because we have potentially a whole lot
of Widgets, we want to optimize the storage of WidgetCategories. In
the database, WidgetCategory uses a composite-id made up of Name and
Type -- so any new Widget saved (or old Widget updated) should only
insert a new WidgetCategory if it does not already exist -- and if the
category does exist -- it should reference that. It should never be
updated (and probably never deleted).
We accomplished this by having the WidgetCategory mapped using a
composite-id, but referencing an auto-incrementing identity column in
the database. This mapping works great with new Widget object -- it
will either find the existing WidgetCategory, or insert a new one.
However it fails on the update of an existing Widget with a new (or
existing) WidgetCategory.
<class name="Widget" table="Widget" lazy="false">
<id name="ID">
<generator class="native"/>
</id>
<property name="Name" />
<property name="Data" />
<many-to-one name="MyCategory" column="CategoryId"
class="WidgetCategory" cascade="save-update" property-
ref="PersistanceID">
</many-to-one>
</class>
--------------
<class name="WidgetCategory" table="Category" lazy="false"
mutable="false">
<composite-id>
<key-property name="CategoryName"/>
<key-property name="CategoryType" />
</composite-id>
<property name="PersistanceID" column="PersistanceID" type="int"
generated="always"/>
</class>
----------------
I have an existing Widget, modify its WidgetCategory [either new or
existing], SaveOrUpdate(widget), during the flush [this error only
occurs when I commit] and cascade of the Widget object I receive this
exception:
TestCase 'Widgets.Test.WidgetTests.InsertNewCategoryIntoOldWidget'
failed: System.InvalidCastException: Unable to cast object of type
'System.Int32' to type 'Widgets.Common.WidgetCategory'.
at (Object , GetterCallback )
\NHibernate\Bytecode\Lightweight
\AccessOptimizer.cs(27,0): at
NHibernate.Bytecode.Lightweight.AccessOptimizer.GetPropertyValues(Object
target)
\NHibernate\Tuple\Component
\PocoComponentTuplizer.cs(60,0): at
NHibernate.Tuple.Component.PocoComponentTuplizer.GetPropertyValues(Object
component)
\NHibernate\Type\ComponentType.cs(277,0): at
NHibernate.Type.ComponentType.GetPropertyValues(Object component,
EntityMode entityMode)
\NHibernate\Type\ComponentType.cs(164,0): at
NHibernate.Type.ComponentType.IsDirty(Object x, Object y, Boolean[]
checkable, ISessionImplementor session)
\NHibernate\Type\ManyToOneType.cs(211,0): at
NHibernate.Type.ManyToOneType.IsDirty(Object old, Object current,
Boolean[] checkable, ISessionImplementor session)
\NHibernate\Type\TypeFactory.cs(1023,0): at
NHibernate.Type.TypeFactory.FindDirty(StandardProperty[] properties,
Object[] x, Object[] y, Boolean[][] includeColumns, Boolean
anyUninitializedProperties, ISessionImplementor session)
\NHibernate\Persister\Entity
\AbstractEntityPersister.cs(3437,0): at
NHibernate.Persister.Entity.AbstractEntityPersister.FindDirty(Object[]
currentState, Object[] previousState, Object entity,
ISessionImplementor session)
\NHibernate\Event\Default
\DefaultFlushEntityEventListener.cs(422,0): at
NHibernate.Event.Default.DefaultFlushEntityEventListener.DirtyCheck(FlushEntityEvent
event)
\NHibernate\Event\Default
\DefaultFlushEntityEventListener.cs(176,0): at
NHibernate.Event.Default.DefaultFlushEntityEventListener.IsUpdateNecessary(FlushEntityEvent
event, Boolean mightBeDirty)
\NHibernate\Event\Default
\DefaultFlushEntityEventListener.cs(43,0): at
NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent
event)
\NHibernate\Event\Default
\AbstractFlushingEventListener.cs(163,0): at
NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent
event)
\NHibernate\Event\Default
\AbstractFlushingEventListener.cs(61,0): at
NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent
event)
\NHibernate\Event\Default
\DefaultFlushEventListener.cs(18,0): at
NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent
event)
\NHibernate\Impl\SessionImpl.cs(1187,0): at
NHibernate.Impl.SessionImpl.Flush()
\NHibernate\Transaction\AdoTransaction.cs(177,0): at
NHibernate.Transaction.AdoTransaction.Commit()
\Widgets.Test\WidgetTests.cs(193,0): at
Widgets.Test.WidgetTests.InsertNewCategoryIntoOldWidget()
It appears that during the dirty check of Widget's properties NHib
attempts to compare the integer property-ref to the original
WidgetCategory with the new WidgetCategory object itself. Again, this
is only on the update of an existing Widget -- on insert of a new
Widget (even with an existing category) NHib works great -- b/c no
dirty checking obviously. This produces the same behavior in 1.2.1.4
and 2.0.1.
Please, any ideas? I'm sure there is probably a better way to
accomplish this objective, I would like to be able to just save the
widget and have all these changes automatically made -- although I
know this might not be possible. The "widget" I am attempt to save has
a few of these type of relationships. Thanks in advance.