Подтвердить что ты не робот

Как сохранить ребенка с назначенным id в 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 я нашел тег версии, который может быть решением, но у меня нет постоянного поля для использования в качестве версии. В этом случае, как я могу сохранить (вставить) дочерний элемент с назначенным идентификатором и его родителем?

4b9b3361

Ответ 1

При каскадировании от родителя к дочернему элементу NHibernate использует метод SaveOrUpdate. Вы правы, что NHibernate необходимо определить способ выполнения вставки или обновления. Он рассмотрит три разных поля для несохраненного значения, чтобы определить, является ли объект новым.

  • Id
  • Версия
  • Отметка

При назначении идентификатора вам понадобится поле Version или Timestamp, чтобы указать, что объект является новым.

Альтернативой может быть вызов Save() для детей явно.

Ответ 2

Я не уверен на 100%, если это та же проблема, что и у вас, но моя база данных на 100% назначена id (тьфу), и мне пришлось создать Interceptor, который отслеживал, является ли ребенок или нет для работы каскадов.

Код cut/paste (вот почему он имеет немые имена... Сначала я не понял 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 не знает, сохраняется ли назначенный объект идентификатора или нет, вы отслеживаете его.

По умолчанию объект начинается с сохранения (_saved) при значении false. Когда объект загружается или сохраняется NHibernate, триггер устанавливает, что объекты сохраняются (_saved) флаг в true.

Итак, для нового элемента, который не сохраняется, он начинается с false и остается false, потому что NHibernate никогда не сохранял и не загружал его. Когда NHibernate проверяет, является ли ребенок переходным, триггер отвечает, что он является временным, и происходит спасение, которое отмечает, что ребенок сохраняется. Также теперь для любого будущего использования потребуется загрузка, которая снова отмечает ее как сохраненную.

Ответ 3

Вызов session.SaveOrUpdate(childObject) должен решить проблему.