1

I have a model, MyModel, with a list property and an accompanying string used to store the values in the database.

Simplifying a bit to increase readability.

public class MyModel
{
    public int ID { get; set; }
    public List<int> Numbers { get; set; }
    [Column, ScaffoldColumn(false)]
    public string NumbersStore { get { /*parses Numbers*/ } set { /*splits the value and sets Numbers accordingly*/ } }
}

I'm using basic CRUD, based off the scaffolding.

In my Create/Edit views, I have manually written a select multiple. Not using helpers was probably a bad idea. I have no problem retrieving these values in the Index, Details, and Delete views, but I cannot figure out how to actually bind this data to my model when creating/editing.

Just from some blind Googling, I've tried:
- Added a list to the MyModelController.Create parameters list
- Attempted to use Request.Form["SelectNameAttribute"]

CSHTML:

@using (Html.BeginForm())
{
    <div class="form-group">
        @Html.LabelFor(model => model.Numbers, htmlAttributes: new { @class = "control-label col-md-2" })
        <select class="select2 col-md-10 form-control" multiple="multiple" id="Numbers" name="Numbers">
            <!-- Ultimately enumerated with a loop that goes through the database, probably butchering MVC conventions. Don't think that's relevant to the question. -->
            <option value="1">One</option>
            <option value="2">Two</option>
            <!-- Etc. -->
        </select>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
}

Controller:

public ActionResult Create([Bind(Include = "ID,NumbersStore")] MyModel myModel) // Changing NumbersStore to Numbers does nothing
{
    //myModel.NumbersStore = Request.Form["Numbers"].ToString();
    if (ModelState.IsValid)
    {
        db.MyModels.Add(myModel);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(myModel);
}
Sinjai
  • 1,085
  • 1
  • 15
  • 34
  • Could you also include in the post how you are setting up the select multiple? – Brian Mains Jun 14 '17 at 18:33
  • Did you try `@using (Html.Beginform("foo","bar", FormMethod.Post...` it could help if you post your cshtml – lloyd Jun 14 '17 at 18:37
  • 1
    Including more of your code from your view and controller will make it easier for us to help you. – David Lee Jun 14 '17 at 18:41
  • Of course, sorry, it's been a long day. Edited. – Sinjai Jun 14 '17 at 18:46
  • @Sinjai In your controller you need to include the Numbers in your Bind `[Bind(Include = "ID,Numbers")]` let me know if this fixes it and I will post it as an answer. =) – David Lee Jun 14 '17 at 18:48
  • @maccettura Added that, apologies. – Sinjai Jun 14 '17 at 18:50
  • @DavidLee Do you know how Bind plays with Entity Framework database contexts, if at all? Currently `NumbersStore` is in bind (I'll update my post), but presumably never actually set anywhere and I'm not sure how to go about that. – Sinjai Jun 14 '17 at 18:53
  • @Sinjai All `[Bind(Include="")]` does is tell the controller what values you want to retrieve from your post for a given model. So in your case it will only get `ID` for your `MyModel`. `[Bind(Include="")]` is there by default for security purposes it will work without it. To test to see if the `[Bind(Include="")]` is an issue I would remove it and see if your values are being populated within your `MyModel`. If so add it back and play with the values in the Include string. – David Lee Jun 14 '17 at 18:56
  • @Sinjai For your update you are showing `[Bind(Include="ID, NumbersStore")]` but in your form in your razor view your select list has an `id` and `name` of `Numbers`. As a result your controller wont pick up the values from your post – David Lee Jun 14 '17 at 19:00
  • @DavidLee If only it were that simple. Perhaps it's a typing problem? Maybe it can't convert between whatever it uses for the `select` and my `System.Collections.Generic.List`. – Sinjai Jun 14 '17 at 19:37
  • Did you take a look at [Post values from a multiple select](https://stackoverflow.com/questions/11616659/post-values-from-a-multiple-select) – lloyd Jun 14 '17 at 20:34
  • @Sinjai please have a look at my answer and let me know if it's a help. – lloyd Jun 14 '17 at 20:54
  • @DavidLee Could you post the "change the `Bind` string" as an answer? That was the stupidly easy fix that I thought I'd already tried. I'm willing to bet I had a name somewhere not lining up -- either in the `name` attribute, the `Bind`, or the property. – Sinjai Jun 16 '17 at 17:42
  • @Sinjai Glad to hear you were able to resolve your issue. Thank you for letting me know that I was able to help. I have posted an answer summarizing the various comments. – David Lee Jun 16 '17 at 18:42

3 Answers3

0

You can pass values to the controller just my simply assigning a parameter with the same name as the html tag:

You can give your select a name (notice the brackets for a list of items)

<select class="select2 col-md-10 form-control" multiple="multiple" id="Numbers" name="Numbers[]">
    <option value="1">One</option>
    <option value="2">Two</option>
</select>

Then in controller use parameter int[] Numbers to access the posted values

[HttpPost]
public ActionResult Create([Bind(Include = "ID,NumbersStore")] MyModel myModel, int[] Numbers)
{
    // do something with numbers

    db.MyModels.Add(myModel);
    db.SaveChanges();
    return RedirectToAction("Index");
}
lloyd
  • 1,089
  • 1
  • 17
  • 39
  • Does that array even populate without also being in the `Bind`? Changing `NumbersStore` to `Numbers` does the trick, even though I thought I already did that -- I'm assuming a name somewhere didn't line up, either the `name` attribute, the `Bind` string, or the model's property. – Sinjai Jun 16 '17 at 17:39
0

Lloyd's answer got me close.

I look like a fool here. The answer is as simple as naming the select and binding that same name to the controller action -- something I could have sworn I'd done, but I think I bound the store rather than the actual list.

I'm not sure if the name attribute has to be the same as the name of the model's property, but I did it to be safe (and for clarity).

So the correct code is:

MyModel.cs

public class MyModel
{
    public int ID { get; set; }
    public List<int> Numbers { get; set; } // This is the list we're changing
    [Column, ScaffoldColumn(false)]
    public string NumbersStore { get { /*parses Numbers*/ } set { /*splits the value and sets Numbers accordingly*/ } }
}

MyModelView.cshtml

@using (Html.BeginForm())
{
    <div class="form-group">
        @Html.LabelFor(model => model.Numbers, htmlAttributes: new { @class = "control-label col-md-2" })
        <select class="select2 col-md-10 form-control" multiple="multiple" id="Numbers" name="Numbers"> // Notice name attribute
            <option value="1">One</option>
            <option value="2">Two</option>
            <!-- Etc. -->
        </select>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
}

MyModelController.cs .Create

public ActionResult Create([Bind(Include = "ID,Numbers")] MyModel myModel) // Notice name attribute in Bind 
{
    if (ModelState.IsValid)
    {
        db.MyModels.Add(myModel);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(myModel);
}

Note:
- Create does not need an array as a parameter (unless you need to make further changes)
- The name HTML attribute does not need to end with [] -- as described here by Sergey, that's a PHP convention that has no effect on ASP.NET
- As far as I know, the name attribute does need to be the same as the property that is eventually being mutated

Sinjai
  • 1,085
  • 1
  • 15
  • 34
0

Just recapping the solution from the comments in the question.

The [Bind(Include = "")] needs to include the the properties from the form being posted.

In this case the NumbersStore needs to be Numbers to match the property in the HTML.

[HttpPost]
public ActionResult Create([Bind(Include = "ID,Numbers")] MyModel myModel)
{
    // do something with numbers

    db.MyModels.Add(myModel);
    db.SaveChanges();
    return RedirectToAction("Index");
}
David Lee
  • 2,040
  • 17
  • 36
  • Thanks, brother. That `int` array doesn't seem to be needed unless you need to do some fiddling with those values. Which begs the question, does some background nonsense happen that stores that array to the model? I'm willing to bet the answer's no. So then you could presumably do that yourself, but what's the point of calling it at all if you can just get it from the `myModel` parameter? – Sinjai Jun 16 '17 at 18:46
  • @Sinjai Not an expert at what goes on behind the scenes within the controller, but I believe you are correct. It would only store the int array if it is set as a parameter in your controller. You should also be able to fiddle around with the int values by accessing them from your controller action as such `var numbers = myModel.Numbers`. I typically do not have any parameters in my controller actions other than the model or viewmodel. – David Lee Jun 16 '17 at 18:53
  • It stores without the parameter, hence my confusion. I'll try to find documentation somewhere. – Sinjai Jun 16 '17 at 19:02