【问题标题】:What is the best practice for readonly lists in NHibernateNHibernate 中只读列表的最佳实践是什么
【发布时间】:2010-10-13 08:29:50
【问题描述】:

我正在处理的域模型具有根聚合和子实体。 类似于以下代码:

class Order
{
   IList<OrderLine> Lines {get;set;}
}

class OrderLine
{
}

现在我希望我的订单控制线路。类似的东西:

class Order
{
   OrderLine[] Lines {get;}

   void AddLine(OrderLine line);
}

此时我们正在使用以下模式:

class Order
{
   private IList<OrderLine> lines = new List<OrderLine>();
   OrderLine[] Lines {get {return this.lines;}}

   void AddLine(OrderLine line)
   {
      this.orders.Add(line);
   {
}

NHibernate 直接映射到行字段。

现在问题...

  • 在这种情况下你会怎么练习?
  • 有没有人使用方法: public IEnumerable GetLines()
  • 您使用什么作为属性的返回类型?可能是 ReadOnlyCollection 或 IEnumerable;
  • 这可能不是最好的提问地方吗?请提出建议。

更新:似乎 IEnumerable 获胜,但解决方案仍然不完美...

【问题讨论】:

    标签: .net arrays nhibernate list domain-driven-design


    【解决方案1】:

    我是这样做的:

    public class Order
    {
          private ISet<OrderLine> _orderLines = new HashedSet<OrderLine>();
    
          public ReadOnlyCollection<OrderLine> OrderLines
          {
              get { return new List<OrderLine>(_orderLines).AsReadOnly(); }
          }
    
          public void AddOrderLine( OrderLine ol )
          {
              ...
          }
    }
    

    然后,当然,在映射中,NHibernate 被告知使用 _orderLines 字段:

    <set name="OrderLine" access="field.camelcase-underscore" ... >
    ...
    </set>
    

    【讨论】:

    • 使用您的代码,以下代码将失败: order.OrderLines != order.OrderLines;我不确定这是否不好......无论如何,谢谢。
    • 这没关系恕我直言,因为你不应该那样比较它:​​)
    • 很好的解决方案。这对我帮助很大。谢谢!
    • 为什么选择返回 new 而不是在惰性私有字段中缓存 readonlycollection?
    • 如果您要返回新的,我认为您最好按照其他人的建议执行return new ReadOnlyCollection&lt;OrderLine&gt;(_orderLines)。这是因为.AsReadOnly() 返回一个仍然有Add() 的对象(只是在调用时抛出异常),而ReadOnlyCollection&lt;T&gt;(来自System.Collections.ObjectModel)没有,因此确保没有人考虑使用您的只读收集错误。
    【解决方案2】:

    我使用的模式是:

    class Order
    {
       private List<OrderLine> lines = new List<OrderLine>();
    
       IEnumerable<OrderLine> Lines { get { return this.lines; } }
    
       void AddLine(OrderLine line)
       {
           this.orders.Add(line);
       }
    }
    

    如果您使用的是 NET 3.5,您将获得使用 LINQ 的 IEnumerable 所需的所有搜索功能,并且隐藏您的集合实现。

    返回 OrderLine[] 的问题是您的收藏可以在外部进行修改,例如:

    Order.Lines[0] = new OrderLine().
    

    【讨论】:

    • 关于“Order.Lines[0] = new OrderLine();”这不可以。每次访问 Lines 时都会发出新的数组副本(虽然我不喜欢这样......)。 IEnumerable 缺少列表功能,我的意思是计数,按索引访问。当然,LINQ 涵盖了这一点。谢谢!
    • 在这里,您仍然可以将 Lines 属性转换为 List 并以这种方式修改集合...
    • 是的,但你必须进行选角。与其说是保护,不如说是展示你可以对收藏做什么。如果你想要额外的安全性,你可以返回 this.lines.AsReadonly()。
    • 我同意 gcores 的观点,关键是通过公共界面传达您大声要做的事情。如果你想通过强制转换来破解,你可以这样做,但即使你复制它,也有人可以使用反射来获取底层列表。
    • @Rezler 是的,你必须这样做。
    【解决方案3】:

    我将集合公开为 ReadOnlyCollection,并使用 AddX 和 RemoveX 方法来维护集合。我们刚刚切换到 3.5,我正在考虑改为公开 IEnumerable。在大多数情况下,使用 NHibernate 时,子级都有对父级的引用,因此公开 Add 和 Remove 方法可以让您保持这种关系:

        public void AddPlayer(Player player)
        {
            player.Company = this;
            this._Players.Add(player);
        }
    
        public void RemovePlayer(Player player)
        {
            player.Company = null;
            this._Players.Remove(player);
        }
    

    【讨论】:

    • 是的,似乎 IEnumerable 比 ReadOnlyCollection... ReadOnlyCollection 使 API 混乱。它仍然有 Add 方法,会在运行时抛出异常...
    • 迈克——我认为你错了。返回 List.AsReadOnly() 确实公开了 Add 方法,但 ReadOnlyCollection 没有。 ReadOnlyCollection 位于 System.Collections.ObjectModel 中,因此经常被忽略。
    • 有趣,是的,这是真的.. 我忽略了它。非常感谢。
    【解决方案4】:

    如果我要公开一个不应修改的列表,那么我将使用 IEnumerable 和 yield。我发现尝试将 ReadOnlyCollections 与 NHiberante 结合使用很麻烦。

    使用这种方法,您仍然拥有通过 NHibernate 映射和填充的专用线路字段;但是,对集合的公共访问是通过迭代器执行的。您不能使用此 Lines 属性在基础列表中添加或删除。

    例如:

    public IEnumerable<OrderLine> Lines {
        get {
            foreach (OrderLine aline in lines) {
                yield return aline;
            }
        }
    }
    

    【讨论】:

    • 为什么不只是“返回行”?你是保护铸造?使用 yield 的解决方案意味着 Count() 将需要 foreach 并将加载所有支持的项目(在延迟加载的情况下)。
    • IEnumerable 与 yield 相结合,让您可以遍历集合,而不能添加/删除项目。
    • 好吧,IEnumerable 本身也会阻止您添加/删除项目 - 所以public IEnumerable&lt;OrderLine&gt; Lines { get { return lines; }} 就足够了。
    【解决方案5】:

    我花了几天时间寻找 NHibernate 中只读列表的最佳方法。 这次讨论对我形成适合我们项目的讨论帮助很大。

    我开始使用一种方法:

    1. 支持字段用于存储集合
    2. IEnumerable 用于公开集合以强制客户端使用 AddLine() 和 RemoveLine() 方法。
    3. 除了 IEnumerable 之外,还使用 ​​ReadOnlyCollection 类型。

    代码:

    public class Order
    {
        private readonly IList<OrderLine> lines = new List<OrderLine>();
    
        public virtual IEnumerable<OrderLine> Lines
        {
            get
            {
                return new ReadOnlyCollection<OrderLine>(lines);
            }
        }
    
        public void AddLine(OrderLine line)
        {
            if (!lines.Contains(line))
            {
                this.lines.Add(line);
                line.Order = this;
            }
        }
    
        public void RemoveLine(OrderLine line)
        {
            if (lines.Contains(line))
            {
                this.lines.Remove(line);
                line.Order = null;
            }
        }
    }
    
    public class OrderLine
    {
        public Order Order { get; set; }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-19
      • 2020-06-26
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      • 1970-01-01
      • 2011-06-07
      相关资源
      最近更新 更多