0

I have an input table in my website which is connected to a View Model. In the controller method, I pass this View Model to the controller and vice versa, meaning the controller populates the view model with data from the data base and the view returns a view model populated with form data the user might have entered.

The problem is that once the view received the view model object, the "ID" attribute from the database is no longer there. When the Post method is called, there is no way to know which database entry must be updated.

My question is: How do I update a specific database entry when I pass a view model to the controller method?

Example Controller Method:

[HttpPost]
public ActionResult method(ViewModel vm)
{
    DataContext.Context.Where(x => x.ID == vm.Object.ID) // this is where vm.Object.ID always returns "0", not the actual ID from the database entry
    Context.SaveChanges();
    return View(vm);
}

If you need more information, please let me know. Also, using jquery is not a viable option for this project. Thanks a lot for your help!

Edit:

View:

@model MyANTon.ViewModels.Q4_Answer_VM


@{
    ViewBag.Title = "myANTon Anforderungserfassung";
    ViewBag.HideNavBar = false;
}


@using (Html.BeginForm())

{
    <div class="container">
        <div class="jumbotron">

            <hr />
            <table class="grid" id="datatable">
                <tr>
                    <th>Nr.</th>
                    <th>Last</th>
                    <th>Quelle</th>
                    <th>Ziel</th>
                    <th>Frequenz [/h]</th>
                    <th>Abstand [m]</th>
                    <th></th>
                    <th></th>

                    @{int i = 1; }

                    @for  (var a = 0; a < Model.Matrix.Count; a++)
                    {
                    <tr>
                        <td>@(i++)</td>
                        <td>@Html.TextBoxFor(x => Model.Matrix[a].Load)</td>
                        <td>@Html.TextAreaFor(x => Model.Matrix[a].Source)</td>
                        <td>@Html.TextAreaFor(x => Model.Matrix[a].Goal)</td>
                        <td>@Html.TextAreaFor(x => Model.Matrix[a].Frequency)</td>
                        <td>@Html.TextAreaFor(x => Model.Matrix[a].Distance)</td>
                        <td><input type="submit" name="+" class="btn btn-default" value="+" /></td>
                        <td><input type="submit" class="btn btn-default" value="-" /></td>

                    </tr>
                    }
                </table>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" class="btn btn-default" name="Speichern" value="Speichern" />
                        <input type="submit" class="btn btn-default" value="Speichern und weiter" />
                        <input type="button" class="btn btn-default" value="Weiter zu Schritt 5" onclick="@("window.location.href='" + @Url.Action("Q_Fifthpage", "Home") + "'");" />
                    </div>   
                </div>  
            </div>
        </div>
 }

GET Method:

    [HttpGet]
        public ActionResult Q_FourthPage()
        {
            // get current Questionnaire ID
            int CurrentQstID = Convert.ToInt32(Session["qstid"]);

            // create vm object. Capacity is a column in the table.
            var Q4ViewModel = new ViewModels.Q4_Answer_VM();

            // look for existing input data columns for this questionnaire in db
            if (db.Capacities.Any(x => x.Questionnaire_ID == CurrentQstID))
            {
                // answers exist
                Q4ViewModel.Matrix.AddRange(db.Capacities.Where(x => x.Questionnaire_ID == CurrentQstID));

            }
            else
            {
                // new capacity matrix
                Q4ViewModel.TMatrix = db.QuestionTexts.Where(x => x.ID == 21).FirstOrDefault();
                Q4ViewModel.Matrix = new List<Models.Capacity>();
            }

            var tmpcapacity = new Models.Capacity();
            tmpcapacity.Questionnaire_ID = Convert.ToInt32(Session["qstid"]);

            Q4ViewModel.Matrix.Add(tmpcapacity);
            db.Capacities.Add(tmpcapacity);
            db.SaveChanges();
            return View(Q4ViewModel);
        }

POST Method:

    [HttpPost]
        public ActionResult Q_FourthPage(ViewModels.Q4_Answer_VM vm)
        {
            int currentQst = Convert.ToInt32(Session["qstid"]);

            if (Request.Form["+"] != null)
            {

                var tmpcapacity = new Models.Capacity();
                tmpcapacity.Questionnaire_ID = currentQst;
                vm.Matrix.Add(tmpcapacity);

                db.Capacities.Add(tmpcapacity);
                db.SaveChanges();

                return View(vm);
            }

            if (Request.Form["Speichern"] != null)
            {
            // save data
                if (!ModelState.IsValid) return View("~/Views/Shared/Error.cshtml");

                var tmpcapacity = new Models.Capacity();

                for (var a = 0; a < vm.Matrix.Count; a++)
                {
                    var current = vm.Matrix[a];
                    current.ID = vm.Matrix[a].ID;
                    if (db.Capacities.Any(x => x.ID == current.ID))
                        // if clause never triggers true
                        // vm does not contain capacity ID
                    {
                        // column exists and is changed (or not)

                        tmpcapacity.Distance = vm.Matrix[a].Distance;
                        tmpcapacity.Frequency = vm.Matrix[a].Frequency;
                        tmpcapacity.Source = vm.Matrix[a].Source;
                        tmpcapacity.Goal = vm.Matrix[a].Goal;
                        tmpcapacity.Load = vm.Matrix[a].Load;
                        Models.Capacity c = db.Capacities.Where(x => x.ID == current.ID).FirstOrDefault();
                        c = tmpcapacity;
                        db.SaveChanges();

                    }
                    else
                    {
                        // new column
                        tmpcapacity.Distance = vm.Matrix[a].Distance;
                        tmpcapacity.Frequency = vm.Matrix[a].Frequency;
                        tmpcapacity.Source = vm.Matrix[a].Source;
                        tmpcapacity.Goal = vm.Matrix[a].Goal;
                        tmpcapacity.Load = vm.Matrix[a].Load;
                        tmpcapacity.Questionnaire_ID = currentQst;

                        db.Capacities.Add(tmpcapacity);
                        db.SaveChanges();
                    }

                }
                db.SaveChanges();
            }    
            return View(vm); 
        }
mneumann
  • 713
  • 2
  • 9
  • 42
  • Show your view model, and how you populate it in the GET method, and how you generate the from control for the ID property –  Oct 30 '17 at 10:52
  • I added the View, Get and Post methods. Edit: The code is very rough, there may be a lot of useless stuff in there still. Sorry. – mneumann Oct 30 '17 at 11:05
  • 1
    What does that edited code have to do with your original question? I can only assume that you now want the value for `current.ID = vm.Matrix[a].ID;`? Which would always be the default for its type since you never create a form control for it in the view. And you never change data by calling `.SaveChanges()` in a GET method! –  Oct 30 '17 at 11:13
  • It seems what your really trying to do here is dynamically add item to a collection? In which case your going about it all wrong. Refer [this answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) for some options –  Oct 30 '17 at 11:15
  • @StephenMuecke I appreciate your comments. Right now, I will use the less optimal solutions from below, but I will try and optimize this a few weeks from now. The dynamic addition of objects works and was not the primary focus of this question. Yes, I wanted the value for the current ID - I had a bug in the code where I created a temporary object each time, which had the ID o "0". The second object worked, though, using the "HiddenFor" helper. This is where I realized my mistake and, using the HiddenFor field, got this to work. Thanks again for your input. – mneumann Oct 30 '17 at 12:27

2 Answers2

1

If you want to "save" the object ID and get it back when the post occurs, you need to store it into a hidden field using the .HiddenFor() HTML helper - something like this:

@using (Html.BeginForm())
{
    @Html.HiddenFor(m => m.Object.Id);

    <div class="container">
        <div class="jumbotron">

Then, upon your POST, you should get back the Object.ID in your post body and you should be able to tell which object this is for.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
1

If you need to bind the ID to the Model then you need to use hidden filed under the form when you are using Razor.

@Html.HiddenFor(model => model.Id)

For more details

@using (Html.BeginForm("method", "ControllerName", FormMethod.Post))
{
@Html.HiddenFor(Model=>Model.ID)

<div class="form-group">
    @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
    <div class="col-md-10">
        @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
    </div>
</div>
<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <input type="submit" class="btn btn-default" value="Button" />
    </div>
</div>
}

Now you can access ID at your controller action method.

  • My view model contains a list of elements which would all need a HiddenFor. The number of elements is dynamic. Would i need to loop over the elements in the view? How would I do that? Thanks for your answer. – mneumann Oct 30 '17 at 11:29
  • As per your comment, you want to persevere the value of some field in the view model(dynamic) and don't want to show them on view and when you post back to the action method, you want them in controller action method. Then you can make the hidden filed dynamically by the loop in the same form and when you will submit, you will find that value in action method. You can also store that value in session if your server will have a good RAM. – Mohit Sharma Oct 30 '17 at 11:50
  • I got this to work. I added a `@Html.HiddenFor(x => Model.Matrix[a].ID);` within the loop. Thank you very much! – mneumann Oct 30 '17 at 12:25