views:

38

answers:

1

Hello SO,

If got a couple of linqTOsql objects that I am trying to edit through a form.

First, there's the Stream class:

[Table]
public class Stream
{
    [HiddenInput(DisplayValue = false)]
    [Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
    public long StreamID { get; set; }

    /*other properties removes for brevity*/

    // relationship (one stream to many Stream2FieldTypes) 
    // uses EntitySet<Stream2FieldTypes> and OtherKey for the FK in Stream2FieldTypes 
    private EntitySet<Stream2FieldTypes> _Stream2FieldTypes;
    [System.Data.Linq.Mapping.Association(Storage = "_Stream2FieldTypes", OtherKey = "StreamID")]
    public EntitySet<Stream2FieldTypes> Stream2FieldTypes
    {
        get { return this._Stream2FieldTypes; }
        set { this._Stream2FieldTypes.Assign(value); }
    }

}

Then there's the Stream2FieldTypes class:

[Table]
public class Stream2FieldTypes
{
    [Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
    public long s2fID { get; set; }

    /*other properties removed for brevity*/

    [Column]
    public long StreamID { get; set; }     // FK




    // relationship (one Stream2FieldTypes to Streams) 
    private EntitySet<Stream> _Stream;
    [Association(Storage = "_Stream", ThisKey = "StreamID")]
    public EntitySet<Stream> Stream
    {
        get { return this._Stream; }
        set { this._Stream.Assign(value); }
    }
}

I use the linqTOsql repository to get these objects and send them to the view like this:

    public ActionResult StreamEdit(long id)
    {
        Genesis.Domain.Entities.Stream stream = genesisRepository.Streams.FirstOrDefault(x => x.StreamID == id);

        return View(stream);
    }

Everything get's sent to the view without a problem... But when the form on the view is submitted, the Stream.Stream2FieldTypes gets lost for some reason. I catch the form submission with this action:

    [HttpPost]
    public ActionResult StreamEdit( long id, Genesis.Domain.Entities.Stream form)
    {

            return View(form);
    }

And the view is without the Stream2FieldTypes collection.

The view is writing the Stream2FieldTypes like this:

<% for (int i = 0; i < (Model.Stream2FieldTypes != null ? Model.Stream2FieldTypes.Count() : 0); i++) %>
<% { %>
    <div class="ListItem">
        Label: <%: Html.EditorFor(x => x.Stream2FieldTypes[i].s2fLabel, new { style="width:100px" })%>
        Required: <%: Html.EditorFor(x => x.Stream2FieldTypes[i].s2fIsRequired)%>
        Field Type ID": <%: Html.EditorFor(x => x.Stream2FieldTypes[i].FieldTypeID, new { style="width:20px;" })%>
        Stream ID: <%: Html.EditorFor(x => x.Stream2FieldTypes[i].StreamID) %>
</div>
<% } %>

Why isn't the default model binder in mvc correctly binding the nested collection?

+1  A: 

I would recommend you using POCO view models instead of those cluttered model classes. They are not adapted to the view and are difficult to work with. Trying to bind to LINQ specific collections like EntitySet<T> is not a good idea. This being using an editor template might actually work but even if this works bear mind that it should be a temporary solution until you introduce proper view models.

In your main view replace the loop with this:

<%: Html.EditorFor(x => x.Stream2FieldTypes) %>

and then add a strongly typed partial view in ~/Views/Home/EditorTemplates/Stream2FieldTypes.ascx:

<%@ Control 
    Language="C#" 
    Inherits="System.Web.Mvc.ViewUserControl<SomeNs.Stream2FieldTypes>" 
%>
<div class="ListItem">
    Label: 
    <%: Html.EditorFor(x => x.s2fLabel, new { style="width:100px" }) %>

    Required: 
    <%: Html.EditorFor(x => x.s2fIsRequired) %>

    Field Type ID: 
    <%: Html.EditorFor(x => x.FieldTypeID, new { style="width:20px;" })%>

    Stream ID: 
    <%: Html.EditorFor(x => x.StreamID) %>
</div>

Normally this should take care of generating correct input names, but to avoid people taking this as a good example: I repeat once again use proper view models.

Darin Dimitrov
@Darin - Thanks for the tips. I had not considered using an editing template. In respect to POCO objects... regardless of whether `EditorFor<T>` works or not, I should duplicate my linqTOsql objects as Plain Old C# Objects? Of course, I'm new to this level of databinding but that seems like a really bad idea to have essentially two copies of the same structure.
quakkels
@quakkels, it is not the same structure. View models are classes that are adapted to the view. You could have different and multiple view model classes for a single model class like the `Stream` class you have. For example you could have one view model for the edit view and another one for the show view because of different fields, etc. To simplify the mapping between your view model and model classes you could use [AutoMapper](http://automapper.codeplex.com/). The idea is to never pass the real model to the view but have a specially tailored view model class for this particular view.
Darin Dimitrov
Because view models are adapted to the view using them will lead to very clean code in your view because the view model will already have everything that's needed and you won't have to write ugly loops, conditions, etc. in your markup which leads to spaghetti code. You shouldn't be worried about duplicating some properties in the view models, that's what those classes are meant for.
Darin Dimitrov
@Darin - Once again, thanks for the info. I'll experiment with that when I get back on Monday.
quakkels
@Darin - It still did not work with the EditorTemplate. However, it did work when I Converted the linqTOsql objects to POCO EditModels (ViewModels). I am really a little frustrated that it didn't work with the linq objects. I've been studying Steven Sanderson's book Pro ASP.NET MVC 2 Framework and he uses his linq objects in his views so as to have the database structure and validation rules all in one place. It also just seems to be less code in general that way. But, I guess the bottom line is that my form binds with POCO and doesn't with LINQ... I just wish I knew why.
quakkels