My domain classes that have one-to-many mappings generally take the following form (untested code):
public Customer Customer
{
// Public methods.
public Order AddOrder(Order order)
{
_orders.Add(order);
}
public Order GetOrder(long id)
{
return _orders.Where(x => x.Id).Single();
}
// etc.
// Private fields.
private ICollection<Order> _orders = new List<Order>();
}
The EF4 code-only samples I’ve seen expose a public ICollection when dealing with one-to-many relationships.
Is there a way to persist and restore my collections with exposing them? If not, it would appear that my domain objects will be designed to meet the requirements of the ORM, which seems to go against the spirit of the endeavour. Exposing an ICollection (with it’s Add, etc. methods) doesn’t seem particularly clean, and wouldn’t be my default approach.
Update
Found this post that suggests it wasn’t possible in May. Of course, the Microsoft poster did say that they were “strongly considering implementing” it (I’d hope so) and we’re half a year on, so maybe there’s been some progress?
I found that whatever was done, EF requires the
ICollection<T>to be public. I think this is because when the objects are loaded from the database, the mapping looks for a collection property, gets the collection and then calls theAddmethod of the collection to add each of the child objects.I wanted to ensure that the addition was done through a method on the parent object so created a solution of wrapping the collection, catching the add and directing it to my preferred method of addition.
Extending a
Listand other collection types was not possible because theAddmethod is not virtual. One option is to extendCollectionclass and override theInsertItemmethod.I have only focussed on the
Add,Remove, andClearfunctions of theICollection<T>interface as those are the ones that can modify the collection.First, is my base collection wrapper which implements the
ICollection<T>interfaceThe default behaviour is that of a normal collection. However, the caller can specify an alternative
Addmethod to be called. In addition, the caller can enforce that theAdd,Remove,Clearoperations are not permitted by setting the alternatives tonull. This results inNotSupportedExceptionbeing thrown if anyone tries to use the method.The throwing of an exception is not as good as preventing access in the first place. However, code should be tested (unit tested) and an exception will be found very quickly and a suitable code change made.
Given that base class we can use it in two ways. Examples are using the original post objects.
1) Create a specific type of wrapped collection (For example,
List)public class WrappedListCollection : WrappedCollectionBase, IList
{
private List innerList;
This can then be used:
2) Give a collection to be wrapped using
which can be used as follows:
{
public ICollection Orders {get { return _wrappedOrders; } }
// Public methods.
There are some other ways to call the
WrappedCollectionconstructorsFor example, to override add but keep remove and clear as normal
I agree that it would be best if EF would not require the collection to be public but this solution allows me to control the modification of my collection.
For the problem of preventing access to the collection for querying you can use approach 2) above and set the WrappedCollection
GetEnumeratormethod to throw aNotSupportedException. Then yourGetOrdermethod can stay as it is. A neater method however may be to expose the wrapped collection. For example:Then the call in the
GetOrdermethod would become