NHibernate: дочерний составной идентификатор не обновляется при использовании merge () - PullRequest
0 голосов
/ 25 ноября 2010

Отображение:

    <class name="PhoneTypeTest" lazy="false" table="PhoneType">
  <cache usage="read-write"/>
  <id name ="Id" type="Int32" unsaved-value="0">
    <generator class="identity"/>
  </id>

  <bag name="Resources" table="PhoneTypeResource" lazy="false" cascade="all" inverse="true">
    <key column="PhoneTypeId" />
    <one-to-many class="PhoneTypeTestResource" not-found="ignore"/>
  </bag>  

</class>

<class name="PhoneTypeTestResource" lazy="false" table="PhoneTypeResource">
  <composite-id class="CCCC.ResourcesCompositeKey, DDDD" name="Id">
    <key-property name="OwnerId" column="PhoneTypeId"/>
    <key-property name="CultureId"/>
  </composite-id>
  <property name="Name"/>
</class>

Объекты:

public class PhoneTypeTest
{
    public PhoneTypeTest()
    {
        Resources = new List<PhoneTypeTestResource>();
    }

    public virtual int Id { get; set; }
    public virtual IList<PhoneTypeTestResource> Resources { get; set; }
}

public class PhoneTypeTestResource
{
    public virtual ResourcesCompositeKey Id { get; set; }
    public virtual string Name { get; set; }
}

Юнит тест:

        var ent = new PhoneTypeTest();
        ent.Resources.Add(new PhoneTypeTestResource { Id = new ResourcesCompositeKey { CultureId = En, OwnerId = 0 }, Name = "Name" });

        Session.Merge(ent);
        Session.Flush();
        Session.Clear();

SQL, сгенерированный nHib:

-- statement #1
INSERT INTO PhoneType
DEFAULT VALUES


select SCOPE_IDENTITY()


-- statement #2
SELECT phonetypet0_.PhoneTypeId as PhoneTyp1_61_0_,
       phonetypet0_.CultureId   as CultureId61_0_,
       phonetypet0_.Name        as Name61_0_
FROM   PhoneTypeResource phonetypet0_
WHERE  phonetypet0_.PhoneTypeId = 0 /* @p0 */
       and phonetypet0_.CultureId = 'en' /* @p1 */

-- statement #3
INSERT INTO PhoneTypeResource
           (Name,
            PhoneTypeId,
            CultureId)
VALUES     ('Name' /* @p0 */,
            0 /* @p1 */,
            'en' /* @p2 */)

-- statement #4
ERROR: 
Could not synchronize database state with session

ТАК, как вы можете видеть, проблема в том, что nHib НЕ обновляет дочерний составной идентификатор после того, как его родительский объект был сохранен, и попытка сохранить дочерние элементы не удалась. Зачем?? Как я могу сделать nHib для обновления этих идентификаторов? Кроме того, если я использую SaveOrUpdate () вместо Merge (), он работает нормально !! Но я должен использовать слияние. Пожалуйста, помогите!

Ответы [ 2 ]

3 голосов
/ 29 ноября 2010

Я закончил вручную обновлять дочерние идентификаторы после сохранения - просто еще один обходной путь ошибки nHibernate. Не используйте составные идентификаторы или, что еще лучше, не используйте nHibernate ..

0 голосов
/ 15 декабря 2011

У меня была точно такая же проблема.Мне пришлось запустить обновление некоторых сообщений, прежде чем я сделаю вставку.Затем, когда я собирался сделать вставку, я получил ошибку, что объект уже используется.

SaveOrUpdate дал мне эту ошибку, и ofc Update тоже.Слияние сработало, НО это сохранило только родительский объект, а не дочерние объекты.

Соединение, однако, заключается в том, что дочерний объект является ответственным за сохранение (в mapobject.

  HasMany(x => x.Info)
            .Cascade.SaveUpdate();

, так что я думаю, что честно, он вроде работает как задумано.

Раздражает то, что если вы используете SaveOrUpdate для родительского объекта, он на самом деле обновит и дочерние элементы, поскольку он понимает соединение. Но если вы используете Merge, он вообще не будет понимать соединение!

Вот немного этого кода

public AlertEntity InsertCapAlerts(AlertEntity capMessage)
    {

        using (var transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted))
        {
            var getCap = GetCapAlert(capMessage.Id);
            if (getCap != null)
            {
                InfoEntity infoToSave = capMessage.Infos.FirstOrDefault();
                _session.Merge(infoToSave);
                _session.Merge(capMessage);
            }
            else
                _session.Save(capMessage);

            transaction.Commit();
            return capMessage;
        }
    }

По сути, я сделал следующее: когда я получаю свое новое capMessage, которое я собираюсь сохранить, я сначала проверяю, существует ли он уже в БД.тогда нет, тогда хорошо, просто сделайте Save (). Но если он существует (вот где SaveOrUpdate всегда падал), тогда я просто создаю новый экземпляр дочерней записи (у нас есть только один дочерний элемент, поэтому FirstOrDefault ()).

Первая попытка, которую я только что сохранил объект Child в БД с помощью Merge (потому что SaveOrUpdate по-прежнему выдает ошибку из-за объекта, использованного в предыдущем сеансе, и да, ябуду пробовать session.Clear () и Flush, а что нет).Это сработало, но потом я заметил, что мой дочерний объект больше не указывает на своего родителя.Таким образом, дочерний объект - теперь сирота, плачущая на улицах базы данных.Поэтому нам нужно снова указать идентификатор родительского объекта на дочерний объект.

InfoEntity infoToSave = capMessage.Infos.FirstOrDefault();
infoToSave.AlertEntity_id = capMessage.Id;
_session.Merge(infoToSave);
_session.Merge(capMessage);

Почему вы спрашиваете?Потому что nHibernate очень симпатичен тем, что когда он запускает Merge, он не говорит о дочерних записях (как это происходит при запуске SaveOrUpdate, как я упоминал ранее).Поэтому, когда вы используете Merge, вы должны обновить все дочерние посты самостоятельно и снова указать на Родителя.

Если кто-то знает лучшее решение для этого.Пожалуйста, сообщите мне.В противном случае, я надеюсь, что этот небольшой пример кода кому-нибудь поможет:)

...