11

I'm trying to make a website using asp.net mvc 4 & EF6 where I want to update multiple rows all at once. But for some reason, it's not working & I get an error like this,

System.NullReferenceException: Object reference not set to an instance of an object

Here are my codes,

Controller

[HttpPost]
    public ActionResult MakeDue(List<BillCheck> BillLists)
    {
        if (Session["username"] != null)
        {
            if (ModelState.IsValid)
            {
                foreach (var BillId in BillLists)
                {
                    var getDue = db.BillChecks.Where(p => p.id == BillId.id).FirstOrDefault();
                    getDue.due = BillId.due;
                }
                db.SaveChanges();
                return RedirectToAction("Success");
            }
            else
            {
                return RedirectToAction("Failed");
            }
        }
        else
        {
            return RedirectToAction("Login");
        }
    }

View

@using (Html.BeginForm("MakeDue", "Home"))
{
    @Html.ValidationSummary(true)
    @foreach(var item in Model.DueList)
    {
        @Html.HiddenFor(modelItem => item.id)
        <tr>
            <td>@Html.DisplayFor(modelItem => item.flat)</td>
            <td>@Html.DisplayFor(modelItem => item.name)</td>
            <td>@Html.TextBoxFor(modelItem => item.due)</td>
        </tr>
    }
    <input type="submit" class="btn btn-success" value="Update" />
}

Is there something wrong in my code? How can I update all the inputs for due given at once?

Shihan Khan
  • 2,180
  • 4
  • 34
  • 67
  • turns out in your controller `getDue` can be null. – tweray May 22 '15 at 17:50
  • What does `DueList` and `BillList` look like? – Justin Helgerson May 22 '15 at 18:23
  • What line are you getting the exception on? –  May 22 '15 at 20:23
  • Almost all cases of `NullReferenceException` are the same. Please see "[What is a NullReferenceException and how do I fix it?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net)" for some hints. – John Saunders May 23 '15 at 00:15
  • 2
    @JohnSaunders, please read my question very carefully. I had a specific problem which highly does not relate to `NullReferenceException`. All I wanted to know is updating multiple records at once. Your given post has not the solution for me. So it's not duplicate. – Shihan Khan May 23 '15 at 06:35
  • Since you got a `NullReferenceException`, you _do_ have the same problem. It doesn't matter what you were trying to accomplish. The fact that something is `null` but you used it anyway is what's preventing you from accomplishing it. There's nothing so special about updating multiple records at once. – John Saunders May 23 '15 at 06:43

1 Answers1

27

Your first problem is that your use of a foreach loop is generating duplicate name attributes, which will not bind to a collection, and as a result the BillLists parameter will always be an empty collection (its also generating duplicate id attributes which is invalid html). You need to use a for loop or a custom EditorTemplate for typeof BillCheck. Using a for loop, your view need to be

using (Html.BeginForm("MakeDue", "Home"))
{
  @Html.ValidationSummary(true)
  @for(int i = 0; i < Model.DueList.Count; i++)
  {
    
    <tr>
      <td>
        @Html.HiddenFor(m => m.DueList[i].id)
        @Html.DisplayFor(m => m.DueList[i].flat)</td>
      <td>@Html.DisplayFor(m => m.DueList[i].name)</td>
      <td>@Html.TextBoxFor(m => m.DueList[i].due)</td>
    </tr>
  }
  <input type="submit" class="btn btn-success" value="Update" />
}

Note also that the @Html.HiddenFor() helper need to be inside a <td> element in order to be valid html.

The next problem is that the model in the view is not typeof List<BillCheck>, but it does contain a property named DueList, which is typeof List<BillCheck> so your POST method needs to be

public ActionResult MakeDue(YourModel model)

where YourModel is the class name you used to generate the view (i.e. in the @model ??? statement). Then you loop in the controller method need to be

foreach (var BillId in model.DueList)
{
  var getDue = db.BillChecks.Where(p => p.id == BillId.id).FirstOrDefault();
  if (getDue != null) // add this
  {
    getDue.due = BillId.due;
  }
}
db.SaveChanges();

Note also the addition of the if (getDue != null) check.

Side note: Your are checking if (ModelState.IsValid). It is recommended you return the view if ModelState is not valid so that the user can correct any errors.

Hayee
  • 33
  • 6
  • 1
    This example, and the explanation that go with it, do something that sadly few tutorials haven't done - emphasize the fact that you have to use a for loop (not a foreach) and give an explicit index to each element in the html-list that is generated in order for it to get picked up properly by the controller. Really a great job! – NovaDev Nov 05 '15 at 01:14
  • Also worth noting, @Html.DisplayFor will not post its value to the controller. You need to also use Html.HiddenFor for values you want posted http://stackoverflow.com/questions/12314849/html-displayfor-not-posting-values-to-controller-in-asp-net-mvc-3 – Ethan Fischer Feb 01 '17 at 22:09
  • As I mentioned this solution also works for HTML markup another type - for example `
    ` instead of `tr` and `
    ` instead of `td`.
    – hellboy Jul 07 '17 at 18:31
  • I am having trouble porting this to .net core - did anyone get this working under dotnet core? – PawelKosi Jan 08 '18 at 22:25