Domain Model I am working on has root aggregate and child entities.
Something like the following code:
class Order
{
IList<OrderLine> Lines {get;set;}
}
class OrderLine
{
}
Now I want my Order to control lines. Something like that:
class Order
{
OrderLine[] Lines {get;}
void AddLine(OrderLine line);
}
At this time we are using the following pattern:
class Order
{
private IList<OrderLine> lines = new List<OrderLine>();
OrderLine[] Lines {get {return this.lines;}}
void AddLine(OrderLine line)
{
this.orders.Add(line);
{
}
NHibernate is mapped directly to the lines field.
Now questions...
- What do you practice in such situations?
- Does anyone use methods:
public IEnumerable GetLines()
- What are you using as return type for property? May be ReadOnlyCollection or IEnumerable;
- May be this is not the best place to ask? Suggest please.
Update: Seems IEnumerable wins, however solution still is not perfect...
The pattern I use is:
class Order
{
private List<OrderLine> lines = new List<OrderLine>();
IEnumerable<OrderLine> Lines { get { return this.lines; } }
void AddLine(OrderLine line)
{
this.orders.Add(line);
}
}
If you're using NET 3.5 you get all the search functionality you could want for IEnumerable using LINQ, and you hide your collection implementation.
The problem with returning OrderLine[] is that your collection can be modified externally eg:
Order.Lines[0] = new OrderLine().
I do it like this:
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 )
{
...
}
}
Then, offcourse, in the mapping, NHibernate is told to use the _orderLines field:
<set name="OrderLine" access="field.camelcase-underscore" ... >
...
</set>
I expose collections as ReadOnlyCollection and use AddX and RemoveX methods for maintaining the collections. We just switched to 3.5 and I'm thinking about exposing IEnumerable instead. In most cases with NHibernate the child has a reference to the parent so exposing Add and Remove methods allows you to maintain that relationship:
public void AddPlayer(Player player)
{
player.Company = this;
this._Players.Add(player);
}
public void RemovePlayer(Player player)
{
player.Company = null;
this._Players.Remove(player);
}
If I'm exposing a list which shouldn't be modified, then I use IEnumerable and yield. I find it cumbersome trying to use ReadOnlyCollections in conjunction with NHiberante.
With this approach, you still have the private lines field which gets mapped and populated via NHibernate; however, public access to the collection is performed through iterators. You cannot add to or remove from the underlying list with this Lines property.
For example:
public IEnumerable<OrderLine> Lines {
get {
foreach (OrderLine aline in lines) {
yield return aline;
}
}
}
I have spent several days looking for a best approach for readonly lists in NHibernate.
This discussion helped me a lot to form the one that fits our project.
There is an approach I started to use:
- Backing fields are used to store collections
- IEnumerable< T> is used to expose collections to force clients use AddLine() and RemoveLine() methods.
- ReadOnlyCollection type is used in addition to IEnumerable.
Code:
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; }
}