1
votes

I have been trying to complete a simple application I was given for practice at my summer internship. I have almost no background in ASP.NET so I was given extra work. I am trying to get a database entry to update from an http post function in c#, however when the function runs it throws a validation error. I have been trying to find reasons for the error, however I cannot seem to figure out why it is not working. Any help is appreciated.

Here is the error:

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

Source Error:

Line 130: db.Entry(productNote).State = EntityState.Modified;

Line 131: db.SaveChanges();

Line 132: return RedirectToAction("Index");

Source File: C:\Users\Morph\Documents\Visual Studio 2015\Projects\NotesWebApp\NotesWebApp\Controllers\ProductsController.cs Line: 131

Here is the http post function in my Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "ID,ProductID,NoteText,CreateDate,Archived")] ProductNote productNote)
{
    Response.Write("<script type=\"text/javascript\">alert('Works');</script>");
    if (ModelState.IsValid)
    {
        db.Entry(productNote).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return RedirectToAction("Index");
}

Here is the form I am submitting to the function:

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(noteItem => note.ID)
    @Html.HiddenFor(noteItem => note.ProductID)
    @Html.HiddenFor(noteItem => note.NoteText)
    @Html.HiddenFor(noteItem => note.CreateDate)
    <div class="checkbox">
        @Html.EditorFor(noteItem => note.Archived)
        @Html.ValidationMessageFor(noteItem => note.Archived, "", new { @class = "text-danger" })
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>
}
3
What does See 'EntityValidationErrors' property for more details tell you. And refer How to create a Minimal, Complete, and Verifiable example. 90% of this code is irrelevant to your issueuser3559349
Going to the msdn page for Entity Validation Errors tells me very little about why I am having the problem. I had already visited and tried looking at the informationEllis
You need to break on the exception and look at the EntityValidationErrors propertymitchellJ
Hi @Ellis, you can refer to this to help you get the exact error messages.Shawn Yan
Then your approach is wrong. You can create a form for each item with a route value only for the ID and post to public ActionResult Archive(int ID) (and you could do that using ajax so that the user can continue to archive notes while staying on the same page)user3559349

3 Answers

3
votes

The reason for the error is that you generating forms in a foreach loop and as a result the controls have name attributes that have no relationship to your model (refer HTML Table to ADO.NET DataTable for more information). As a result, your POST method just initializes a new ProductNote with default properties (presumably ID is int so its value will be 0).

Even if you did generate the view correctly, you are degrading your app by generating a whole lot a unnecessary html and posting it back, but more importantly, a malicious user could easily change all the data in your ProductNote table without you knowing.

From your comments, you just wanting to submit a form to mark a single ProductNote as archived. Start by creating a method that represents what you want to do

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Archive (int ID)
{
    // Get the original 
    ProductNote model = db.ProductNote.FirstOrDefault(x => x.ID == ID);
    // If the user has the permission to archive and the model is not null
    model.Archived = true;
    db.SaveChanges();
    return RedirectToAction("Index");
}

And change the view to

@foreach(var note in Model.ProductNotes)
{
    @Html.DisplayFor(m => note.NoteText)
    ....
    @using (Html.BeginForm("Archive", "Products", new { id = note.ID }))
    {
        @Html.AntiForgeryToken()
        <input type="submit" value="Archive" />
    }
}

You will also want to consider handling the forms .submit() event to display a confirm message (or alternatively just use a link to a GET method that displays a confirm page and make the POST from there). In addition, you should consider using ajax to post the ID value, so that you can stay on the same page and continue to archive further ProductNote items.

1
votes

updating the Entity data Model will work

0
votes

try to use

db.Set<ProductNote>().AddOrUpdate(productNote);
db.SaveChanges();

without

db.Entry(productNote).State = EntityState.Modified;