Лучшая практика для обработки семейств родителей и детей NHibernate

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

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 к родительскому классу и сделать ответственным за добавление детей - проблема в том, что есть два способа добавить ребенка через свойство "Дети" и метод. Так это лучшее решение?

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;
    }
}

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

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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.

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

Ответ 6

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

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

< 3!

Ответ 7

Мне не нравятся все дополнительные методы 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);
   }   
}