Вопрос по nhibernate – Как сохранить ребенка с назначенным идентификатором в nhibernate

20

У меня есть два класса:

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

Можете ли вы опубликовать ваши отношения отношений? mxmissile
Не уверен, что это решит проблему или не будет связано с & quot; назначенным & quot; Я никогда не работал с ними. Но попробуйте установить значение inverse = & quot; true & quot; на вашей сумке собственности. mxmissile
Хорошо, я включил их сейчас. Serhat Ozgel

Ваш Ответ

2   ответа
15

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

Код вырезан / вставлен (именно поэтому он имеет тупые имена ... Сначала я не понял 100%!), И первоначально я получил 90% его из онлайн-документации (которую я не могу найти через Google Right сейчас ... извините)

Базовый класс, который вы помещаете в объект с назначенным идентификатором, который хотите каскадировать:

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 и остается ложным, поскольку NHibernate никогда не сохранял и не загружал его. Когда NHibernate проверяет, является ли дочерний процесс временным, триггер отвечает, что он временный, и происходит сохранение, которое отмечает дочерний элемент как сохраненный. Также теперь любое будущее использование потребует загрузки, которая снова помечает его как сохраненный.

это также решило мою проблему. Очень просто и легко наносится.
Этот конкретный метод решил много вопросов для нас. К вашему сведению - документация, в которой этот код находится, поступает прямо из документации NHibernate.
28

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

Id Version Timestamp

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

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

+1 г! Это помогло мне найти решение проблемы с Fluent NHibernate. Мои дочерние объекты вызывали обновление вместо вставки. Чтобы исправить, я должен был добавить к отображенному идентификатору, например,Id(x => x.Id).UnsavedValue(*defaultValue*);  куда*defaultValue* соответствует значению в моем объекте, например int: -1, Guid: Guid.Empty
+1 за наиболее компактное объяснение того, как nhibernate определяет, является ли объект новым или отдельным в сети.
У меня похожая проблема, но не родитель-ребенок. Это просто одна сущность с присвоенными Id (int), которые мне нужно вставить. У меня нет столбцов Version и Timestamp, но я полагаю, что они не нужны для моего случая. Когда я пытаюсь вставить объект (просто вызывая метод Save ()), я получаю то же исключение StaleStateException. NHProf показывает, что инструкция UPDATE отправлена (очевидно, неправильно). Есть идеи, что я могу делать неправильно и почему NHibernate все еще пытается обновить?
Понял! Идентификатор не может быть установлен (должен быть 0) при вызове метода Save, поэтому вы должны использовать перегрузку с предоставленным идентификатором. Видимо Save не всегда вставка ...: o (

Похожие вопросы