2

So I've been working on a simple asp.net application this week. And today I had a problem with a razor view not returning anything to my controller method. The view had to return several objects in a List<>.

I finally got it working by passing a list of objects to the view and looping through them with a 'for' loop. I also tried it today with a foreach loop but that didn't work.

I've got the following GET method that passes a list of empty objects to the view:

// GET: MpSwitches/CreateSeveral
    public ActionResult CreateSeveral()
    {
        var mpSwitches = new List<MpSwitch>
        {
            new MpSwitch() {IpAdress = "1"},
            new MpSwitch() {IpAdress = "2"},
            new MpSwitch() {IpAdress = "3"},
            new MpSwitch() {IpAdress = "4"},
            new MpSwitch() {IpAdress = "5"}
        };
        // Check if the user gets redirected to this method because of a First Time Startup.
        if (TempData["RedirectFirstTimeStartup"] != null)
        {
            ViewBag.FirstTime =
                "Je bent doorgestuurd naar deze pagina omdat er nog geen MP-switches in de database staan.";
            return View(mpSwitches);
        }
        return View(mpSwitches);
    }

Then there is the View:

@model List<WebInterface.Models.MpSwitch>
@{
    ViewBag.Title = "Create several";
}
<h2>Create several</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>MpSwitch</h4>
        @if (ViewBag.FirstTime != null)
        {
            <div class="alert-warning">
                <b>Opgelet! </b>@ViewBag.FirstTime
            </div>
        }
        @for (int i = 0; i < Model.Count; i++)
        {
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model[i].IpAdress, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model[i].IpAdress, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model[i].IpAdress, "", new { @class = "text-danger" })
                </div>
            </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>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

My question is: Why doesn't this same code work with a 'foreach' instead of the 'for' loop?

The code that isn't working currently:

    @foreach (var item in Model)
    {
        <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger"})
            <div class="form-group">
            @Html.LabelFor(modelItem => item.IpAdress, htmlAttributes: new { 
    @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(modelItem => item.IpAdress, new { 
    htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(modelItem => item.IpAdress, "", new 
    { @class = "text-danger" })
            </div>
        </div>
    }

Now when using the foreach loop the following controller method doesn't get a parameter (the parameter is null).

    // POST: MpSwitches/CreateSeveral
    // To protect from overposting attacks, please enable the specific 
    properties you want to bind to, for 
    // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CreateSeveral([Bind(Include = 
    "Id,IpAdress,LastSyncDateTime")] List<MpSwitch> mpSwitches)
    {
        if (ModelState.IsValid)
        {
            foreach (var mpSwitch in mpSwitches)
            {
                db.MpSwitches.Add(mpSwitch);
            }
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(mpSwitches);
    }

And i get the following error: Link to image of my error

5
  • 1
    Could you post the code which is not working? Commented Apr 11, 2017 at 14:00
  • code with foreach! Commented Apr 11, 2017 at 14:00
  • I added the not working code Commented Apr 11, 2017 at 14:06
  • Any error are you getting ? Commented Apr 11, 2017 at 14:16
  • Added a link to a screenshot of my error Commented Apr 11, 2017 at 14:23

3 Answers 3

1

if you look at the rendered html when using foreach loop you will get this

<input class="form-control text-box single-line" id="item_IpAdress" name="item.IpAdress" value="" type="text">

and when you use for loop it will be like this

<input class="form-control text-box single-line" id="MpSwitch_0__IpAdress" name="MpSwitch[0].IpAdress" value="" type="text">

since you are using List<MpSwitch> so when you submit the form using for loop in MpSwitch[0].IpAdress new value of IpAddress will be assigned to MpSwitch on index 0 while in foreach loop value will be assigned to item.IpAdress and when you submit List<MpSwitch> will be null because to bind complex objects, you have to provide an index for each item. you can also check this Model Binding To A List

Sign up to request clarification or add additional context in comments.

Comments

0

The reason you're not getting is that when you use a foreach instead of a for, you don't have an index of the item and the names/ids of the HTML elements overlap each other. Take a look at the HTML source that you're sending to the browser (View Page Source or Inspect Element), and you'll notice that the name is MpSwitch[0].IpAddress, etc. When you use a foreach it will only be MpSwitch.IpAddress. That can't be serialized into a collection.

Comments

0

The MVC ModelBinder needs the index of the list items to correctly bind them to the ViewModel.

If you use a for loop, the correct index is incorporated into the id and name attributes of the generated HTML <input> elements.

You can also incorporate the index explicitly, see Binding arrays with missing elements in asp.net mvc.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.