.net NHibernate中只读列表的最佳实践是什么

.net NHibernate中只读列表的最佳实践是什么,.net,arrays,nhibernate,list,domain-driven-design,.net,Arrays,Nhibernate,List,Domain Driven Design,我正在处理的域模型具有根聚合和子实体。 类似于以下代码: class Order { IList<OrderLine> Lines {get;set;} } class OrderLine { } 目前,我们正在使用以下模式: class Order { private IList<OrderLine> lines = new List<OrderLine>(); OrderLine[] Lines {get {return this.l

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

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

class OrderLine
{
}
目前,我们正在使用以下模式:

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

   void AddLine(OrderLine line)
   {
      this.orders.Add(line);
   {
}
类顺序
{
私有IList行=新列表();
OrderLine[]行{get{返回this.Lines;}}
无效添加行(订单行)
{
此.orders.Add(行);
{
}
NHibernate直接映射到lines字段

现在问题

  • 在这种情况下你会做什么
  • 是否有人使用以下方法: 公共IEnumerable GetLines()
  • 您使用什么作为属性的返回类型?可以是ReadOnlyCollection或IEnumerable
  • 也许这不是最好的提问地点?请建议
更新:似乎IEnumerable赢了,但是解决方案仍然不完美…

我使用的模式是:

class Order
{
   private List<OrderLine> lines = new List<OrderLine>();

   IEnumerable<OrderLine> Lines { get { return this.lines; } }

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

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

    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和yield。我发现将ReadOnlyCollections与NHiberante结合使用很麻烦

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

例如:

public IEnumerable<OrderLine> Lines {
    get {
        foreach (OrderLine aline in lines) {
            yield return aline;
        }
    }
}
公共IEnumerable行{
得到{
foreach(订单行中的aline){
收益率;
}
}
}
我是这样做的:

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 )
      {
          ...
      }
}
公共类秩序
{
私有ISet_orderLines=new HashedSet();
公共只读集合医嘱行
{
获取{返回新列表(_orderLines).AsReadOnly();}
}
公共无效添加医嘱行(医嘱行ol)
{
...
}
}
当然,在映射中,NHibernate被告知使用_orderLines字段:

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

...

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

我开始使用一种方法:

  • 支持字段用于存储集合
  • IEnumerable用于公开集合,以强制客户端使用AddLine()和RemoveLine()方法
  • 除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; }
    }
    
    公共类秩序
    {
    私有只读IList行=新列表();
    公共虚拟可数线
    {
    得到
    {
    返回新的只读集合(行);
    }
    }
    公共无效添加行(订单行)
    {
    如果(!line.Contains(line))
    {
    this.lines.Add(行);
    line.Order=this;
    }
    }
    公共无效删除行(订单行)
    {
    if(行.包含(行))
    {
    此。行。删除(行);
    line.Order=null;
    }
    }
    }
    公共类命令行
    {
    公共秩序{get;set;}
    }
    
    为什么不只“返回行”?您是在保护不被强制转换?解决方案的收益率意味着Count()将需要foreach并将加载所有备份的ites(在延迟加载的情况下).IEnumerable与yield相结合,允许您在不添加/删除项的情况下遍历集合。好的,IEnumerable本身也会阻止您添加/删除项-因此,
    public IEnumerable行{get{return Lines;}
    应该足够了。使用您的代码,以下代码将失败:order.OrderLines!=order.OrderLines;我不确定这是否是错误的…无论如何,谢谢。这并不重要,因为您不应该这样比较:)很好的解决方案。这对我帮助很大。谢谢!您为什么选择返回新的而不是缓存ReadOnlyCollectionn在一个懒惰的私有字段中?如果您要返回new,我认为最好按照其他人的建议执行
    返回新的ReadOnlyCollection(\u orderLines)
    。这是因为
    .AsReadOnly()
    返回一个仍然有
    添加()
    (调用时只是抛出异常),而
    ReadOnlyCollection
    (来自System.Collections.ObjectModel)没有,因此确保没有人考虑错误地使用只读集合。关于“Order.Lines[0]=new OrderLine();”它不能。每次访问这些行时,都会发布新的数组副本(但我不喜欢这样…).IEnumerable缺少列表功能,我的意思是计数,按索引访问。当然,LINQ也包括了这一点。谢谢!在这里,您仍然可以将Lines属性强制转换为列表并以这种方式修改集合…是的,但您必须执行强制转换。这不是关于保护,而是关于显示允许对集合执行的操作。Y您可以返回此.lines.AsReadonly()如果你想获得额外的安全性。我同意gcores的观点,关键是要通过公共界面大声传达你要做的事情。如果你想通过施放进行黑客攻击,你可以这样做,但即使你复制它,也有人可以使用反射来获取底层列表。@Rezler是的,你必须这样做。是的,似乎是IEnumerable beats ReadOnly Collection…ReadOnlyCollection使API变得混乱。它仍然有Add方法,在运行时会引发异常…Mike--我认为您在这方面搞错了。返回列表。AsReadOnly()确实公开了Add方法,但ReadOnlyCollection没有。ReadOnlyCollection位于System.Collections.ObjectModel中,因此它经常被忽略。很有趣,是的,这是真的。我忽略了它。非常感谢。
    <set name="OrderLine" access="field.camelcase-underscore" ... >
    ...
    </set>
    
    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; }
    }