1

How can I bind a multi select dropdown list to a list property of a manually added intermediate table?

Classes

public class Department
{
    public virtual int Id { get; set; }
    public virtual IList<Lam> Lams { get; set; }
}

public class Person
{
    public virtual int Id { get; set; }
    public virtual IList<Lam> Lams { get; set; }
}

public class Lam
{
    public virtual int Id { get; set; }
    public virtual Department Department { get; set; }
    public virtual Person Person { get; set; }
}

ViewModel

public class DepartmentCreateEditViewModel
{
    public Department Department { get; set; }
    public IList<Person> Persons { get; set; }
}

ActionResult

    public ActionResult Create()
    {
        // Get all Persons
        var persons = repositoryPerson.GetAll();

        // Create ViewModel
        var viewModel = new DepartmentCreateEditViewModel() { Department = new Department(), Persons = persons };

        // Display View
        return View(viewModel);
    }

Create View

I tried to add a ListBox like this.

@Html.ListBoxFor(model => model.Department.Lams, new SelectList(Model.Persons, "Id", "Name"), new { @class = "form-controll" })

To save the object I want to get back a Department object

[HttpPost]
public ActionResult Create(Department department)

The binding from the dropdown (with persons) to the IList is not working. How am I supposed to do this? Is this even possible?


[Edit] Code after Erik's suggestion

Create ActionResult

[HttpPost]
public ActionResult Create(DepartmentCreateEditViewModel viewModelPostBack)

View

@Html.ListBoxFor(model => model.Department.Lams, new MultiSelectList(Model.Persons, "Id", "Name"), new { @class = "form-controll" })

What I get back:

viewModelPostBack
{EmpLaceMgmt.ViewModels.DepartmentCreateEditViewModel}
    Department: {EmpLaceMgmt.Models.Department}
    Persons: null
viewModelPostBack.Department
{EmpLaceMgmt.Models.Department}
    Id: 0
    Lams: Count = 0

The generated HTML looks like this

<select class="form-controll" id="Department_Lams" multiple="multiple" name="Department.Lams">
    <option value="1">Example Person</option>
</select>
samvdst
  • 618
  • 7
  • 22
  • You seem to be confused about whether you want a dropdown or a listbox, which is it? – Erik Funkenbusch Feb 04 '15 at 16:21
  • Doesn't matter. I just need to be able to select multiple Persons which then are stores in my intermediate table. But I don't know how to do the binding the right way. – samvdst Feb 04 '15 at 16:26
  • I use the web.api/JSON to interchange the data. In that scenario, the person represents the root object, and lam is dependent collection. This way, I can in one shot pass the root plus its related items in collection. Not sure about mvc. Check: http://stackoverflow.com/a/28031239/1679310 – Radim Köhler Feb 05 '15 at 13:08
  • @radim I tryed to create a Lam object and give it a reference to Department & Person. I also set cascade to all. After adding the Lam object to Department.Lams and trying to save, I get the following error message: Cannot insert the value NULL into column 'id', table '...\DATABASE.MDF.dbo.Lam'; column does not allow nulls. INSERT fails. The statement has been terminated. Although I set Id to auto-increment on Lam object. – samvdst Feb 06 '15 at 13:33
  • If the Id column is IDENTITY - and your mapping uses Native generator all must work. Other words, this message seems to be about wrong setting in DB. BUt you are almost there. Double check that your column ID is IDENTITY(1,1). The xml should contain ` – Radim Köhler Feb 06 '15 at 13:36
  • @radim I'm ashamed to tell you, what the problem was. The mapping was all correct (Generators.Native), but I forgot to update the database schema. :( You can copy your answer from the other post, or link to it and I will select your answer. – samvdst Feb 06 '15 at 13:56
  • I am so glad to see that you make it. Congratulations! You have done great job. Did not give up! I am proud of you. Good luck with NHibernate... sir! ;) – Radim Köhler Feb 06 '15 at 13:57

1 Answers1

1

You have three problems. First, you are trying to bind to an IList<T>, but that won't work because the model binder won't know what kind of concrete object it should create to satisfy that... There are many objects that support IList<T>, so which one?

Secondly, You need to use a MultiSelectList and not a SelectList in your helper.

Third, you are posting back a different model type than you are using to create your pages. And that type has a very different structure. In the structure that you create your page with, your data is created with the naming of Department.Lams (because Department is a property of your ViewModel) but in your Post action takes a Department model which, the binder would be looking for an object simply called Lams, not Department.Lams.

So, convert your models to use a concrete type, such as List<Lam>, then post back to your ViewModel rather than Department, and extract the department out of the ViewModel, and finally change your helper to this:

@Html.ListBoxFor(model => model.Department.Lams, 
    new MultiSelectList(Model.Persons, "Id", "Name"), new { @class = "form-controll" })
Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • I only wrote IList in the title, because I wanted to make the question more general. My Department already has a property of type IList. I changed the SelectList to a MultiSelectList & changed the ActionResult to expect the ViewModel. But it does not seem to work. I will update my question with my new code. – samvdst Feb 05 '15 at 09:31
  • @soumer - You didn't understand what I wrote. I suggest you read it again more carefully. – Erik Funkenbusch Feb 05 '15 at 14:56
  • Ok. Your second point is clear. I changed that. Your first point: I should change my model property from `IList` to `List` right? But then I get the following error message: Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag`1[EmpLaceMgmt.Models.Lam]' to type 'System.Collections.Generic.List`1[EmpLaceMgmt.Models.Lam]'. @erik – samvdst Feb 05 '15 at 15:19
  • @soumer - then you shouldn't be passing entities directly to the view or using them in model binding, and instead creating similar view model objects and mapping them to your entities with something like AutoMapper. This is illustrating my point anyways.. nHibernate is expecting some kind of GenericBag object rather than a List anyways, how exactly is MVC supposed to know to create that? – Erik Funkenbusch Feb 05 '15 at 15:21