5

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.

Erik Öjebo
  • 10,821
  • 4
  • 54
  • 75
Chris Stavropoulos
  • 1,766
  • 13
  • 27

5 Answers5

2

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

i.e:

public CustomList<AnotherObject> Items    
{      
    return new CustomList<AnotherObject>(items); 
}

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

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

Adriano Carneiro
  • 57,693
  • 12
  • 90
  • 123
Alex James
  • 20,874
  • 3
  • 50
  • 49
  • 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 May 05 '09 at 18:58
  • The current solution I'm working with has a wrapper class that implements both IList and IList. The non-generic methods have been written to do type-checking. But basically it just wraps an internal IList. – Chris Stavropoulos May 11 '09 at 17:12
1

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
  • 182,639
  • 35
  • 285
  • 343
  • 1
    I have to use IList in order to accommodate NHibernate but Windows Workflow requires List. – Chris Stavropoulos May 05 '09 at 18:40
  • The NHibernate.Collection.Generic.PersistentGenericBag 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 May 05 '09 at 19:00
  • 1
    No matter what you do you're going to lose type-safety. This is why List 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 if that's the case? – Adam Robinson May 05 '09 at 19:03
  • 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 May 05 '09 at 19:07
0

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
  • 13,282
  • 4
  • 35
  • 52
  • 1
    This doesn't work because the underlying object in the IList is an NHibernate.Collection.Generic.PersistentGenericBag which isn't directly castable to a List – Chris Stavropoulos May 05 '09 at 18:54
0

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
  • 48,427
  • 16
  • 81
  • 117
0

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
  • 19,959
  • 4
  • 61
  • 86