views:

340

answers:

3

I have a simple Poco-Model using abstract classes, and it seems not to work with the Default ModelBinder of Asp.net MVC 2. One Item has several Objects in a collection, all using the same abstract base class.

Model:

    public partial class Item
    {  
        public virtual ICollection<Core.Object> Objects
        {
            get
            {
                return _objects;
            }
            set
            {                
                    if (value != _objects)
                    {
                        _objects = value;
                    }
            }
        }
        private ICollection<Core.Object> _objects;
    }   


 public abstract partial class Object
    {

        public virtual Item Item
        {
            get
            {
                return _item;
            }
            set
            {
                if (!Object.ReferenceEquals(_item, value))
                {
                    _item = value;
                }
            }
        }
        private Item _item;       

    }

    public partial class TextObject : Object
    {
        public virtual string Text { get; set; }
    }

Instance:

var NewItem = new Item();                    
var TextObject1 = new TextObject
{
   Text = "Text Object Text",
   Item = NewItem
};                      
List<Core.Object> objects = new List<Core.Object>(){TextObject1};            
NewItem.Objects = objects;

Using the Default Html.EditorForModel() Helper for that item with one TextObject in the Objects collection I get a html input-field like that:

<input class="text-box single-line" id="Objects_0__Text" name="Objects[0].Text" type="text" value="Text Object Text" />

When posting back to the Controller I get an "Cannot create an abstract class" Errormessage from the Default ModelBinder. Obviously the binder tries to instantiate that abstract base class. But I don't know why, since there are is only an object of the inherited TextObject type in the collection. Is there any other way to get this working without writing a custom Modelbinder?

+1  A: 

You will have to use a custom model binder or use viewmodels. The model binder only knows about the type that you use as a parameter to your action (contains the abstract class). Then it tries to map values from the request to that model. There is no way the binder could know that it should use some other implementation and what implementation that might be.

My advice is to create viewmodels that are simpler and map them using automapper.

Mattias Jakobsson
Thanks, I'll give automapper a try. Would you recommend having n lists of derived object types then in the viewmodel?(List<TextObject>, List<BoolObject>, etc.)
A: 

I think you´ll have to specify in the Item class the Objects collection as an ICollection<TextObject>. Otherwise, it would be impossible for the default model binder to realize what object it should create. If you decide to create a custom model binder, you´ll need to add in the form, for every Object, a field indicating its type. Regards.

uvita
Since it's a TPT EF model I need to stick with the ICollection<Object> abstract definition. Seems that the custom modelbinder is the only solution.
A: 

Check out the DerivedTypeModelBinder in MvcContrib. The TypeStamping injects metadata into the view rendering, giving the derivedtypemodelbinder the information needed to make the proper call on what object to instantiate. A longer discussion with links to this

http://stackoverflow.com/questions/2416395/mvc2-modelbinder-for-list-of-derived-objects/2540751#2540751

S. Hebert