views:

1057

answers:

6

I'm trying to build an object that looks something like this:

  public class MyObject
  {
    private IList<AnotherObject> items;
    public List<AnotherObject> Items
    {
      return items.AsEnumerable().ToList<AnotherObject>();
    }
  }

I'm using NHibernate as my DAL and have it mapping directly to the items field and all that works fine.

I'm also using Windows Workflow and the replicator activity doesn't work with the generic IList. (http://social.msdn.microsoft.com/Forums/en-US/windowsworkflowfoundation/thread/2ca74b60-fd33-4031-be4b-17a79e9afe63) This is basically forcing me to use the List<> wrapper instead of the IList<>. This of course breaks the direct NHibernate mapping as NHibernate's IList implementation can't be cast directly to a List.

** EDIT: The Windows Workflow requirement actually means I'm going to lose type-safe access to the list no matter what as it requires an IList.

Now the goal is to serialize/deserialize this object. This works fine with binary serialization but the underlying NHibernate proxy objects explode with nhibernate errors when I try to deserialize them.

So I tried xml serialization. The serialization works fine and gives me my nice concrete class definitions in the serialized xml file which strips out the nhibernate proxies completely. However, when attempting to deserialize this, I'm unable to add the items to the list as the items.AsEnumerable.ToList call won't let items get added to the underlying list via the .Add method.

Does anyone have any thoughts on this? Am I going about this the wrong way?

** EDIT: The NHibernate concrete class is NHibernate.Collection.Generic.PersistentGenericBag which does indeed implement IList directly. However, I lost all the type-safe benefits of the generic list. This puts me back in the realm of having to write a wrapper for each child object and I really wanted to avoid that if possible.

+1  A: 

Yes, unfortunately you can't go about it this way. Calling ToList() creates a brand new instance of the list, so when you add items to that instance they aren't going to be reflected in the original list (as you've clearly discovered).

I don't use NHibernate, but I would be curious to see if your container implements IList (the non-generic version). From the thread you referenced, it appears that System.Collections.IList is what's actually required (and that's implemented by List<T>, which is why it works). Does your container implement IList?

Adam Robinson
I have to use IList<T> in order to accommodate NHibernate but Windows Workflow requires List<T>.
Chris Stavropoulos
I edited my post after rereading your question. Does this help?
Adam Robinson
The NHibernate.Collection.Generic.PersistentGenericBag<T> does indeed implement IList. I could use this but then I lose the type-safety of the generic list which I didn't want to do. The alternative is to write a wrapper as suggested by Alex below, but it's not my ideal solution.
Chris Stavropoulos
No matter what you do you're going to lose type-safety. This is why List<T> doesn't implement IList; it requires that you open an interface that will syntactically allow you to add an element that doesn't actually match the type of the generic. Are you going to be accessing this IList on the "other side" of the serialization? Would you not be able to cast it back to an IList<T> if that's the case?
Adam Robinson
I hadn't thought about that actually. Based on the required integration with Windows Workflow, I don't have any choice but to lose the type-safe accessor anyway. I've got a few ideas spinning around I'm going to have a look at and I'll get back to this question with my findings. Thanks.
Chris Stavropoulos
+2  A: 

On option is to create your own CustomList implementation that is wrapper around an instance that implements IList

i.e: public CustomList Items
{
return new CustomList(items); }

i.e. when you add to your CustomList it adds to the backing list.

It sounds like so long as your class implements IList as well as IList you will be fine.

Alex James
I'm contemplating the wrapper route but I didn't really want to have to write a wrapper for each child object in my domain model. What's the point of generics then? :) I'll admit this is probably a last resort solution though.
Chris Stavropoulos
The current solution I'm working with has a wrapper class that implements both IList and IList<T>. The non-generic methods have been written to do type-checking. But basically it just wraps an internal IList<T>.
Chris Stavropoulos
A: 

Can't you just cast it like this?

public class MyObject
{
    private IList<AnotherObject> items;
    public List<AnotherObject> Items()
    {
        return (List<AnotherObject>)items;
    }
}

Havent had the chanse to try it out, but I think it should work!

fredrik
This doesn't work because the underlying object in the IList is an NHibernate.Collection.Generic.PersistentGenericBag<T> which isn't directly castable to a List<T>
Chris Stavropoulos
Ah, ok, then never mind my answer :S
fredrik
A: 

I think the NHibernate PersistentBag (non-generic) collection implements IList so you could type items as IList instead of IList<AnotherObject>. The link in your question states that the problem is that replicator requires an IList which List<T> implements but IList<T> does not (go figure).

Jamie Ide
See Adam's comment above as to why IList<T> does not implement IList. I hadn't thought about it before myself.
Chris Stavropoulos
A: 

Can it be cast to IEnumerable<T>? You could try this:

public class MyObject
{
    private IList<AnotherObject> items;
    public List<AnotherObject> Items
    {
        return new List<AnotherObject>items.Cast<AnotherObject>());
    }
    // or, to prevent modifying the list
    public IEnumerable<AnotherObject> Items
    {
        return items.Cast<AnotherObject>();
    }
}
Chris Doggett
I need an IList for the Windows Workflow.
Chris Stavropoulos
A: 

Hi Chris,

Did you (or anyone else) get this to work and, if so, how? I am faced with a similar problem and hesitant to implement some complex workaround...

Cheers, Fedor

Fedor Steeman