NHibernate сохранить дочерний элемент FK становится NULL

Итак, у меня есть список дочерних элементов моего родительского объекта, и я хочу сохранить их на своем SQL Server. Когда я запускаю приложение в первый раз, все дочерние элементы получают свой FK правильно, но когда я запускаю его снова и новый родитель не добавляется, новый дочерний элемент (существующего родителя) не получает родительский FK, просто NULL . Как я могу сопоставить родительский FK с моим дочерним сопоставлением для таких ситуаций?

Я пробовал метод Inverse(), но, поскольку мне нужно, чтобы родительский ключ был сгенерирован, все дочерние элементы все равно становятся нулевыми. Мне нужно что-то вроде, если родитель новый, то родитель обновит свои дочерние FK, но когда только дочерний элемент новый, мне нужно, чтобы он выполнял метод Inverse(), возможно ли это?

Еще немного информации: каждый раз, когда я вызываю метод ParentPersist, он каскадируется по мере необходимости. Я добавил метод AddChild() для установки ParentId, когда новый дочерний элемент добавляется в список, он работает так, как я его отлаживал, поэтому дочерний элемент правильно устанавливает свой ParentId.

Объекты выглядят следующим образом:

public class Parent
{
    public virtual int Id { get; set; }
    ...
    public virtual IList<Child> Children{ get; set; } 


 public virtual void AddChild(Child ch)
    {
        ch.IdParent = this.Id;
        Children.Add(ch);
    }
 }

 public class Child
 {
    public virtual int Id { get; set; }
    ...
    public virtual int IdParent {get;set;} 
 }

И мое отображение:

public class ParentMapping : ClassMap<Parent>
{
    public ParentMapping ()
    {
        Id(cso => cso.Id).GeneratedBy.Identity();
        ...
        HasMany(cso => cso.Children).KeyColumn("IdParent").Cascade.SaveUpdate().Not.LazyLoad();
    }
}

public class ChildMapping : ClassMap<Child>
{
    public ChildMapping ()
    {
        Id(cso => cso.Id).GeneratedBy.Identity();
        ...
    }
}

person Markissimo    schedule 15.11.2014    source источник


Ответы (1)


Ваша логика (например, метод Add() в Parent, сопоставление Inverse()) была в порядке. Ты был почти там. Есть только одно НО...
В общем, правильным (если не только правильным) решением является использование объектов для выражения реальности и не только значения ValueType/int. Вот почему мы называем это ORM — объектно-реляционное сопоставление.

Объект в C# должен выглядеть так:

public class Parent
{
    ...
    // correct mapping of the children
    public virtual IList<Child> Children{ get; set; } 

    // this method uses the below updated Child version
    public virtual void AddChild(Child ch)
    {
        // this is replaced
        // ch.IdParent = this.Id;
        // with this essential assignment
        ch.Parent = this;
        Children.Add(ch);
    }
 }

public class Child
{
    ...
    // instead of this        
    // public virtual int IdParent {get;set;} 
    // we need the reference expressed as object
    public virtual Parent Parent { get; set; } 
}

Итак, теперь, когда у нас есть объекты, мы можем настроить сопоставление следующим образом:

// parent
public ParentMapping ()
{
    ...
    HasMany(cso => cso.Children)
       .KeyColumn("IdParent")
       .Inverse() // this is essential for optimized SQL Statements
       .Cascade.SaveUpdate() // All delete orphan would be better
       .Not.LazyLoad();
}
...
// Child
public ChildMapping ()
{
    ...
    References(x => x.Parent, "IdParent"); // it is a to use Inverse() 
}

С этой бизнес-моделью и сопоставлением (Inverse(), назначение обоих отношений заканчивается методом Add()...) NHibernat будет иметь достаточно информации, чтобы всегда (вставлять, обновлять) выдавать правильные операторы SQL.

ПРИМЕЧАНИЕ: Можно спросить, зачем сопоставлять Parent Parent { get; set; }, а не только int IdParent { get; set; }... На самом деле, если бы у нас был существующий Родитель (с НЕ временным идентификатором, т.е. > 0) - разницы не будет. Трюк/проблемы появятся при вставке new Parent. Почти всегда назначение дочерних элементов происходит до того, как родительский объект сохраняется (сбрасывается), и его идентификатор получается из БД (идентификатор сервера sql). И это могло бы/вызвало бы child.IdParent == 0 ...
Мы должны помнить, что вообще - ORM относится к объектам, т.е. отношение представлено ссылочными типами.

person Radim Köhler    schedule 16.11.2014
comment
Работал отлично! Спасибо за решение и объяснение! - person Markissimo; 16.11.2014
comment
Приятно это видеть, наслаждайтесь потрясающим NHibernate ;) - person Radim Köhler; 16.11.2014