处理NHibernate父子集合的最佳实践

时间:2009-06-03 10:29:50

标签: nhibernate domain-driven-design

因此,在一个典型的模型中,您的父母可以拥有许多孩子,而且孩子只能拥有一个父母,那么如何管理孩子的添加。我一直在使用这种方法;

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方法添加到父类,并使其负责添加子项 - 现在的问题是有两种方法可以通过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 个答案:

答案 0 :(得分:10)

这根本不是NHibernate的问题。

您应该实现AddChild方法。这些类对它们的一致性负责,所以它们不应该暴露任何不可用的东西。例如,应隐藏(可变)子列表。公开IEnumerable是个好主意。

你的第二个代码是一个很好的起点。您可能需要更多方法,例如RemoveChild或CoundChildren。

答案 1 :(得分:5)

我这样做:

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

所以,事实上,这也是Stefan所说的。我只是暴露了Children列表的只读副本,这样您就可以轻松地遍历父项的子项,并获得父项的子项数。 添加和删​​除父项的子项必须使用AddChild&amp; amp; RemoveChild成员方法。

答案 2 :(得分:2)

我是这样做的,除了我不使用私有列表属性

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

答案 3 :(得分:1)

我正在使用公共IEnumerable和Add | Remove methods方法。

但是我不喜欢它,因为它不直观,并且对类定义进行了调查。

我想知道为什么人们不使用CustomCollection来覆盖Add,Remove,Replace函数? (就像在MS代码中到处都做的那样)????

答案 4 :(得分:0)

我支持此问题的已接受解决方案,但是所提供的解决方案并不完整,因为使用私有字段需要在映射器中进行一些额外配置。为了其他人的利益,以下是完整的解决方案:

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属性上还有其他选项,允许使用各种命名配置。

答案 5 :(得分:0)

如果您的数据库中有外键,并且您正在使用Identity(SQL Server)生成主键,那么您将 NEED 从子项到父项的反向链接。否则,插入内容会对孩子抱怨,因为nhibernate需要为父ID来回做一些,但它还没有完成。

我们最终做了什么来摆脱反向链接:使用NHibernate HiLo生成器。 这样,NHibernate总是具有插入父/子关系所需的ID。

&lt; 3!

答案 6 :(得分:-1)

我不喜欢所有额外的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);
   }   
}