2010-11-25 3 views
0

Mapping:NHibernate: ребенок композиционно-идентификатор не обновляется при использовании слияния()

<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(), он отлично работает !! Но я должен использовать слияние. Пожалуйста помоги!

ответ

3

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

0

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

SaveOrUpdate дал мне эту ошибку и из-за обновления. Merge работал, но он сохранил только родительский объект, а не дочерние объекты.

Соединение, однако, что дочерний объект является ответственным, чтобы выполнить сохранение (в 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, который я собираюсь сохранить, я сначала проверяю, существует ли он в db. Если это не так, то хорошо, просто сделайте Save(). Но если он существует (вот где SaveOrUpdate всегда разбился), я просто создаю новый экземпляр дочерней записи (у нас есть только один ребенок, для чего FirstOrDefault()).

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

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

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

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

Смежные вопросы