Как сохранить ребенка с назначенным идентификатором в nhibernate
У меня есть два класса:
public class Parent
{
public virtual long? ID { get; set; } // native
public virtual IList<Child> Children { get; set; }
public virtual string Name { get; set; }
}
public class Child
{
public virtual long ID { get; set; } // assigned
public virtual string Name { get; set; }
}
Создание и сохранение родителя и ребенка:
child = new Child() { ID = 1, Name = "SomeName" };
parent = new Parent() { Children = new List() { child } };
session.Save(parent);
Что дает мне:
NHibernate.StaleStateException: неожиданное количество строк: 0; ожидается: 1.
Я думаю, что проблема с назначенным идентификатором на ребенка. Поскольку он имеет идентификатор, NHibernate думает, что ранее сохранял ранее, а это не так.
Сгенерированный (обрезанный и переименованный) SQL:
NHibernate: select child0_.ID as child1_1_, child0_.NAME as NAME1_, child0_.PARENT_ID as COMMAND7_1_, from CHILD child0_
NHibernate: select parent0_.PARENT_ID as parent1_10_
NHibernate: select parent0_.PARENT_ID as parent1_10_, parent0_.NAME as parent2_10_ from PARENT parent0_
NHibernate: UPDATE CHILD SET PARENT_ID = @p0 WHERE CHILD_ID = @p1;@p0 = 2, @p1 = 1
Файлы сопоставления:
<class name="MyNamespace.Child" table="CHILD">
<id name="ID" column="CHILD_ID" type="System.Int64">
<generator class="assigned"></generator>
</id>
<property name="Name" column="NAME"></property>
</class>
<class name="MyNamespace.Parent" table="PARENT">
<id name="ID" column="PARENT_ID" type="System.Int64">
<generator class="native"></generator>
</id>
<property name="Name" column="NAME"></property>
<bag name="Children">
<key column="PARENT_ID"></key>
<one-to-many class="MyNamespace.Child"></one-to-many>
</bag>
</class>
При поиске в Google я нашел тег версии, который может быть решением, но у меня нет постоянного поля для использования в качестве версии. В этом случае, как я могу сохранить (вставить) дочерний элемент с назначенным идентификатором и его родителем?
3 ответа
При каскадном переходе от родителя к потомку NHibernate использует метод SaveOrUpdate. Вы правы, что NHibernate нужен какой-то способ определить, должен ли он выполнять вставку или обновление. Он рассмотрит три разных поля для несохраненного значения, чтобы определить, является ли объект новым.
- Я бы
- Версия
- Отметка
С назначенным идентификатором вам потребуется поле Version или Timestamp, чтобы указать, что объект является новым.
Альтернативой может быть явный вызов метода Save() для дочерних элементов.
Я не уверен на 100%, если это та же проблема, что и у вас, но моей базе данных присвоен 100% идентификатор (тьфу), и мне пришлось создать перехватчик, который бы отслеживал, сохраняется ли ребенок в каскадах или нет работать.
Код вырезан / вставлен (именно поэтому он имеет тупые имена... Сначала я не понял 100%!), И я первоначально получил 90% его из онлайн-документации (которую я не могу найти через Google права сейчас... извините)
Базовый класс, который вы помещаете в объект с назначенным идентификатором, который хотите каскадировать:
public class Persistent
{
private bool _saved = false;
public virtual void OnSave()
{
_saved = true;
}
public virtual void OnLoad()
{
_saved = true;
}
public virtual bool IsSaved
{
get { return _saved; }
}
}
Перехватчик, который вы добавляете в сессию:
public class TrackingNumberInterceptor : EmptyInterceptor
{
public override bool? IsTransient(object entity)
{
if (entity is Persistent)
{
return !((Persistent)entity).IsSaved;
}
else
{
return null;
}
}
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
if (entity is Persistent) ((Persistent)entity).OnLoad();
return false;
}
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
if (entity is Persistent) ((Persistent)entity).OnSave();
return false;
}
}
По сути, идея заключается в том, что, поскольку NHibernate не знает, сохраняется ли назначенный объект id или нет, вы отслеживаете его.
По умолчанию объект начинается с постоянного (_saved) в false. Когда объект загружается или сохраняется NHibernate, триггер устанавливает флаг постоянных объектов (_saved) в значение true.
Таким образом, для нового элемента, который не был сохранен, он начинается с false и остается false, потому что NHibernate никогда не сохранял и не загружал его. Когда NHibernate проверяет, является ли дочерний процесс временным, триггер отвечает, что он временный, и происходит сохранение, которое отмечает дочерний элемент как сохраненный. Также теперь любое будущее использование потребует загрузки, которая снова помечает его как сохраненный.
Вызов session.SaveOrUpdate( childObject) должен решить проблему.