views:

60

answers:

4

Hi all

My domain classes have collections that look like this:

private List<Foo> _foos = new List<Foo>();
public virtual ReadOnlyCollection<Foo> Foos { get { return _foos.AsReadOnly(); } }

This gives me readonly collections that can be modified from within the class (i.e. by using the field _foos).

This collection is mapped as follows (Fluent NHibernate):

HasMany(x => x.Foos).KeyColumn("ParentClassId").Cascade.All().Inverse().Access.CamelCaseField(Prefix.Underscore);

Now when I try to use this collection, I get:

Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag1[Foo]' to type 'System.Collections.Generic.List1[Foo]'.

According to http://stackoverflow.com/questions/1638593/unable-to-cast-object-of-type-nhibernate-collection-generic-persistentgenericbag, this is because the collection needs to be exposed to NHibernate as an interface so that NHibernate can inject one of its own collection classes.

The article suggests using IList instead, but regrettably this interface doesn't include the AsReadOnly() method, messing up my plans to expose only a readonly collection to the outside world.

Can anyone suggest what interface I might use instead, a different approach that meets the same requirements, or an alternative career that doesn't involve this much frustration?

Thanks

David

+1  A: 

The AsReadOnly() method isn't the only way to get a ReadOnlyCollection.

private IList<Foo> _foos = new List<Foo>();
public virtual ReadOnlyCollection<Foo> Foos { get { return new ReadOnlyCollection<Foo>(_foos); } }

Another hoop jumped.

David
A: 

Due to the fact that an IList will not meet your needs and how you're not (luckily) using Automapping, I would set Foos to be a protected/private IList 'NHibernate-friendly' collection, and then create a public ReadOnlyCollection that reads through Foos.

Something like:

    protected IList<Foo> MappableFoos { get; set; }
    public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return new ReadOnlyCollection<Foo>(MappableFoos) } }

    // Mapping file
    HasMany(x => x.MappableFoos ).KeyColumn("ParentClassId").Cascade.All().Inverse().Access.CamelCaseField(Prefix.Underscore);

This way, the only exposed property would be the one I've ridiculously called "ReadOnlyFoos".

Rafael Belliard
Thank you Rafael. This is an interesting approach that I hadn't thought of. I should point out that a different access strategy is required in the mapping for an auto-property's backing field though.
David
+2  A: 

Your answer is a good solution but I just expose collections as IEnumerable<T>. There is a small risk with this approach because these can be cast back to IList. Whether or not that's an acceptable risk depends on the application.

Jamie Ide
Thanks for the heads up. Another user pointed out the IEnumberable thing to me in a previous thread, but I find exposing collections as IEnumerable too limiting - no Count property, no random access, no Contains etc etc.
David
Add `using System.Linq` and you will get almost all that functionality back courtesy of IEnumerable extension methods.
Jamie Ide
A: 

Consider exposing the collection as IEnumerable instead of ReadOnlyCollection; it essentially gives you the same level of protection without having to tie your model to a specific collection implementation. See this article for further discussion.

DanP