Лучшие практики для работы с родительскими и дочерними коллекциями NHibernate - PullRequest
11 голосов
/ 03 июня 2009

Итак, в типичной модели, где у вас есть родитель, у которого может быть много детей, и ребенок, у которого может быть только один родитель, как вы управляете добавлением детей. Я использовал этот подход;

public class Parent
{
    public Parent()
    {
        Children = new List<Child>();
    }

    public IList<Child> Children
    {
        get;
        private set;
    }
}

public class Child
{
    public Parent Parent
    {
        get;
        set;
    }
}

var child = new Child();
var parent = new Parent();
parent.Children.Add(child);
child.Parent = parent;

Проблема в том, что везде, где я хочу добавить нового ребенка, я должен помнить, что нужно добавить ссылку как на ребенка, так и на родителя, и это немного болезненно. Я мог бы просто добавить метод AddChild в родительский класс и назначить его ответственным за добавление дочерних элементов - теперь проблема состоит в том, что есть 2 способа добавить дочерний элемент через свойство Children и метод. Так это лучшее решение?

public class Parent
{
    public Parent()
    {
        children = new List<Child>();
    }

    private IList<Child> children
    {
        get;
        private set;
    }

    public IEnumerable<Child> Children
    {
        get
        {
            return children;
        }
    }

    public void AddChild(Child child)
    {
        children.Add(child);
        child.Parent = this;
    }
}

Существуют ли какие-либо руководства для лучших практик по этому вопросу и чем вы занимаетесь?

Ответы [ 7 ]

10 голосов
/ 03 июня 2009

Это вообще не проблема NHibernate.

Вы должны реализовать метод AddChild. Классы несут ответственность за их согласованность, поэтому они не должны выставлять то, что не должно быть доступно. Например, (изменяемый) список детей должен быть скрыт. Разоблачение IEnumerable - хорошая идея.

Ваш второй код является хорошей отправной точкой. Возможно, вам понадобятся еще несколько методов, например RemoveChild или CoundChildren.

5 голосов
/ 03 июня 2009

Я делаю это так:

public class Parent
{
    private ISet<Child> _children = new HashedSet<Child>();

    public ReadOnlyCollection<Child> Children
    {
        get{ return new List(_children).AsReadOnly(); }
    }

    public void AddChild( Child c )
    {
       if( c != null && !_children.Contains (d) )
       {
          c.Parent = this;
          _children.Add (c);
       }
    }
}

Так что, на самом деле, это немного, что говорит и Стефан. Я просто предоставляю доступную только для чтения копию списка «Дети», чтобы вы могли легко перебирать дочерних элементов родителя и получать количество дочерних элементов, которые есть у родительского элемента. Добавление и удаление дочерних элементов к родителю должно выполняться с использованием методов-членов AddChild & RemoveChild.

2 голосов
/ 03 июня 2009

Я делаю это так, за исключением того, что я не использую свойство для личного списка

private IList<Child> _children
public IEnumerable<Child> Children
{
    get
    {
        return children;
    }
}
1 голос
/ 05 октября 2011

Я использую общедоступный IEnumerable с подходом Add | Remove.

Однако мне это не очень нравится, потому что оно не интуитивно, и определяет класс.

Мне интересно, почему люди не используют CustomCollection, где они переопределяют функции Add, Remove, Replace ?? (как это делается везде в коде MS) ????

0 голосов
/ 05 января 2013

Если у вас есть внешний ключ в вашей БД, и вы используете Identity (SQL Server) для генерации своих первичных ключей, вы собираетесь NEED обратная ссылка от дочернего к родительскому. Иначе, вставка будет жаловаться на дочерний элемент, потому что nhibernate должен сделать кое-что для родительского идентификатора, но он еще этого не сделал.

Что мы в итоге сделали, чтобы избавиться от обратных ссылок: используйте генератор NHibernate HiLo. Таким образом, NHibernate всегда имеет идентификаторы, необходимые для вставки ваших родительских / дочерних отношений.

<3! </p>

0 голосов
/ 20 марта 2012

Я поддерживаю принятое решение этого вопроса, однако представленное решение не является полным, поскольку использование закрытых полей требует некоторой дополнительной настройки в средствах отображения. В интересах других, следующее решение является полным:

public partial class Test
{
    private readonly IList<Child> children = new List<Child>();
    public virtual IEnumerable<Child> Children
    {
        get
        {
            return children;
        }
    }
}

Обратите внимание, что открытая коллекция должна быть виртуальной, чтобы NHibernate мог ее использовать. Мне также нравится делать это поле только для чтения, которое инициализируется при создании класса, чтобы гарантировать его существование во всех сценариях. Вот связанный маппер:

public class TestMap : ClassMap<Test>
{
    ...
    HasMany(s => s.Children).Access.CamelCaseField();
}

Свойство Access указывает NHibernate использовать приватное поле при отображении значений в модель. В свойстве Access есть и другие параметры, позволяющие использовать различные конфигурации именования.

0 голосов
/ 03 июня 2009

Мне не нравятся все дополнительные AddXXX() и RemoveXXX() методы, которые загромождают мои интерфейсы сущностей. Вместо этого у меня есть собственный список, который вызывает события при вызове методов Add() и Remove().

Связывание происходит в обработчиках событий:

public class Course()
{
   public Course()
   {
     this.Topics = new EntityList<Topic>();
     this.Topics.AddItem += new AddItemEventHandler<Topic>(Topic_AddItem);
     this.Topics.RemoveItem += new RemoveItemEventHandler<Topic>(Topic_RemoveItem);
   }

   public EntityList<Topic> Topics { get; private set; }

   private void Topic_AddItem(Topic item, object args)
   {
     // Replace with your linking code:
     EntityLinker.Link(this).With(item, args);
   }   

   private void Topic_RemoveItem(Topic item, object args)
   {
     // Replace with your unlinking code:
     EntityLinker.Unlink(this).From(item, args);
   }   
}
...