1

I am trying to pass an IEnumerable to my controller so that I can loop through each row, make some modifications and add it the database. The problem is that the IEnumerable is Null when it is passed back to the controller.

I have read that I need to change my list to IList and use For instead of ForEach - which I understand..., however, now my IEnumerable needs to be passed to the View as a IList and I'm not sure how to do this.

So, I have a couple of questions.

  1. Is IList the only way to go, can I not pass the model to the controller as an IEnumerable?
  2. If I have to go with IList, how do I change my current AsEnumerable code to send it to the View as IList?

Here is the code before any changes to make it IList were made:

Get controller method:

public ActionResult Register_step6()
{
    var wp = cg.GetUserWorkPoint();
    var model = (from p in db.WorkPointRoles
                 where p.WorkPoint == wp
                 select p).AsEnumerable()
       .Select(r => new RegisterEmployee_Step6Model()
   {
       Role = r.Role,
       isSelected = false,
       RoleDescription = cg.GetRoleDescription(r.Role),
       IconLocation = cg.GetRoleIconLocation(r.Role)
    });

    return View(model);
}

Post controller method:

[HttpPost]
public ActionResult Register_step6(IEnumerable<RegisterEmployee_Step6Model> model)
{
    foreach (var i in model)
    {
        //code in here to update db
    }

    db.SaveChanges();
}

        return RedirectToAction("Index");
    }
}

View:

@model IEnumerable<core_point.Models.RegisterEmployee_Step6Model>

@{
    ViewBag.Title = "Index";
}
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <h2>Select the roles that you have been employed to do, by clicking the check-box...</h2>

    <table class="table">
        @foreach (var item in Model)
        {
            <tr class="h3">
                <td class="vert-align">
                    <img src="@item.IconLocation" height="80" width="80" />
                </td>

                <td class="vert-align">
                    @Html.CheckBoxFor(modelItem => item.isSelected, new { @class = "mycheckBox" })
                </td>

                <td class="vert-align">
                    @Html.DisplayFor(modelItem => item.RoleDescription)
                </td>

                <td class="vert-align">
                    @Html.TextBoxFor(modelItem => item.Role, new { hidden = "hidden" })
                </td>
            </tr>
        }
    
    </table>
    <div>
        Roles selected:<input type="number" id="noSelected" value="0" />
    </div>

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

<script src="~/Scripts/jquery-1.10.2.js"></script>

<script>
    var fld_CheckBoxSelected = document.getElementById("CheckBoxSelected");
    var fld_noSelected = document.getElementById("noSelected");

    $(function(){
        $("input.mycheckBox").change(function(){
            var isSelected = this.checked;

            if (isSelected ) {
                $("#noSelected").val( parseInt($("#noSelected").val())+ 1);
            }
            else
            {
                $("#noSelected").val( parseInt($("#noSelected").val()) -  1);
            }
        });

    });

</script>

View model class:

public class RegisterEmployee_Step6Model
{
    public byte Id { get; set; }
    public int? Role { get; set; }
    public bool isSelected { get; set; }
    public string RoleDescription { get; set; }
    public string IconLocation { get; set; }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ste
  • 59
  • 2
  • 10
  • You can't just change the signature of the controller method to accept an `IList` as your model? – Jeremy Holovacs Aug 09 '16 at 14:51
  • Jeremy, No receive the following message: The model item passed into the dictionary is of type 'System.Linq.Enumerable+WhereSelectEnumerableIterator`2[core_point.Models.WorkPointRole,core_point.Models.RegisterEmployee_Step6Model]', but this dictionary requires a model item of type 'System.Collections.Generic.IList`1[core_point.Models.RegisterEmployee_Step6Model]'. – Ste Aug 09 '16 at 14:55
  • Normally you use `ToArray` or `ToList` instead of the `AsEnumerable()` (in fact, you should probably stay away of `AsEnumerable()` unless you really know what you're doing). Both of them will work as both `IEnumerable` and `IList`, and it will make more sense because you *are* actually materializing the data. In any case you can keep `IEnumerable` in the view in you want, just make sure you follow the MVC rules of [binding to a list](http://stackoverflow.com/q/14822615/11683). – GSerg Aug 09 '16 at 14:57
  • What if you changed the model designator in the view to `IList` as well? – Jeremy Holovacs Aug 09 '16 at 14:59
  • My view has @model IList - should this be changed? – Ste Aug 09 '16 at 15:14
  • `code` var model = ((from p in db.WorkPointRoles where p.WorkPoint == wp select p).AsEnumerable() .Select(r => new RegisterEmployee_Step6Model() { Role = r.Role, isSelected = false, RoleDescription = cg.GetRoleDescription(r.Role), IconLocation = cg.GetRoleIconLocation(r.Role) })).ToList(); `code` – Ste Aug 09 '16 at 15:30
  • If you want to keep it `IEnumerable` then you can use a custom `EditorTemplate` for `RegisterEmployee_Step6Model` - refer [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) –  Aug 09 '16 at 22:50

2 Answers2

0

Thanks for everyone's help.

I changed the Controller Get as follows, I was previously trying to replace the AsEnumerable with ToList()

        var model = ((from p in db.WorkPointRoles
                     where p.WorkPoint == wp
                     select p).AsEnumerable()
       .Select(r => new RegisterEmployee_Step6Model()
       {
           Role = r.Role,
           isSelected = false,
           RoleDescription = cg.GetRoleDescription(r.Role),
           IconLocation = cg.GetRoleIconLocation(r.Role)

       })).ToList();
Ste
  • 59
  • 2
  • 10
  • 1
    This is more complex than it needs to be. You can simply write `var model = (from r in db.WorkPointRoles where r.WorkPoint == wp select new RegisterEmployee_Step6Model { Role = r.Role, isSelected = false, RoleDescription = cg.GetRoleDescription(r.Role), IconLocation = cg.GetRoleIconLocator(r.Role)).ToList();` – Jacob Aug 09 '16 at 15:36
  • The `.AsEnumerable()` doesn't do anything, and you can replace the first select with the second, essentially. – Jacob Aug 09 '16 at 15:37
-1

I found this article that solved the issue for me: https://www.pluralsight.com/guides/asp.net-mvc-getting-default-data-binding-right-for-hierarchical-views

The way in which the View was created, by looping through a for-each structure, created a view (table structure) with rows having the same IDs. This caused the Databinding on the client to fail and NULL was submitted back to the controller.

Once i used the correct for-structure, the data binded correctly and posted to the receiving controller.

This is illustrated in the section "Saving Data" in the link that i provided above. Also illustrated in the sample below.

@if (Model.Orders != null)
{
    for (var i = 0; i < Model.Orders.Count(); i++)
    {
        <tr>
            @Html.HiddenFor(x => Model.Orders[i].CustomerID)
            <td>
                @Html.TextBoxFor(x => Model.Orders[i].OrderID, new { @class = "form-control", @readonly = "readonly" })
            </td>
            <td>
                @Html.TextBoxFor(x => Model.Orders[i].OrderDate, new { @class = "form-control", @readonly = "readonly" })
            </td>
            <td>
                @Html.TextBoxFor(x => Model.Orders[i].Description, new { @class = "form-control" })
            </td>
        </tr>
    }
}
Stefan
  • 187
  • 3
  • 14