views:

197

answers:

3

I was wondering what is the recommended way to expose a collection within a class and if it is any different from the way of doing that same thing when working with NHibernate entities.

Let me explain... I never had a specific problem with my classes exposing collection properties like:

IList<SomeObjType> MyProperty { get; set; }

Having the setter as protected or private gives me some times a bit more control on how I want to handle the collection. I recently came across this article by Davy Brion:

http://davybrion.com/blog/2009/10/stop-exposing-collections-already/

Davy, clearly recommends to have collections as IEnumerables instead of lets say Lists in order to disallow users of having the option to directly manipulate the contents of those collections. I can understand his point but I am not entirely convinced and by reading the comments on his post I am not the only one.

When it comes to NHibernate entities though, it makes much sense to hide the collections in the way he proposes especially when cascades are in place. I want to have complete control of an entity that is in session and its collections, and exposing AddXxx and RemoveXxx for collection properties makes much more sense to me.

The problem is how to do it?

If I have the entity's collections as IEnumerables I have no way of adding/removing elements to them without converting them to Lists by doing ToList() which makes a new list and therefore nothing can be persisted, or casting them to Lists which is a pain because of proxies and lazy loading.

The overall idea is to not allow an entity to be retrieved and have its collections manipulated (add.remove elements) directly but only through the methods I expose while honouring the cascades for collection persistence.

Your advice and ideas will be much appreciated.

A: 

How about exposing them as ReadOnlyCollection?

IList<SomeObjType> _mappedProperty;

return new ReadOnlyCollection<SomeObjType> ExposedProperty
{
  get
  {
    return new ReadOnlyCollection(_mappedProperty);
  }
}
ReadOnlyCollection is an option that I have thought of and it can work quite well in the non NHibernate context. I came accross with mapping issues when using it with NHibernate as it requires to map against a separete private field that has to be initialized when constructing the ReadOnlyCollection property and I was looking for alternatives in the form of a patern ar practice. Thanks for the answer though!
tolism7
A: 

I am using NHibernate and I usually keep the collections as ISet and make the setter protected.

ISet<SomeObjType> MyProperty { get; protected set; }

I also provide the AddXxx and RemoveXxx for collection properties where they are required. This has worked quite satisfactorily for me most of the time. But I will say that there have been instances where it had made sense to allow client code add items to the collection directly.

Basically, what I have seen is if I follow the principle of "Tell, Don't Ask" in my client code, without worrying too much about enforcing rigid access constraints on my Domain Object properties, then I always end up with a good design.

Rohit Agarwal
Thank you for your answer Rohit. As I mentioned in my question I too make the setter protected most of the times but that does NOT disallow client code of using the collection to add/remove elements directy bypassing the logic within the AddXxx/RemoveXxx methods. Protected only prevents client code from completely overriding the whole collection with the new one.
tolism7
+5  A: 

How about...

    private IList<string> _mappedProperty;

    public IEnumerable<string> ExposedProperty
    {
        get { return _mappedProperty.AsEnumerable<string>(); }
    }

    public void Add(string value)
    {
        // Apply business rules, raise events, queue message, etc.
        _mappedProperty.Add(value);
    }

This solution is possible if you use NHibernate to map to the private field, ie. _mappedProperty. You can read more about how to do this in the access and naming strategies documentation here.

In fact, I prefer to map all my classes like this. Its better that the developer decides how to define the public interface of the class, not the ORM.

JulianM
You don't need AsEnumerable<string>, IList<string> already implements IEnumerable<string>
Michael Valenty
That makes much sense. I was aware and advised others regarding mapping private properties to NHinernate but never managed to make the connection for this case. Thanks.
tolism7