-2

I have encountered the error shown as my title. I have tried to search for solutions but all I got is solution about using try catch code blocks.

I have been using a course documentation that I have made to guide me on doing this project but the error that I have encountered this time, I am clueless about which part has gone wrong and how to check the wrong part.

There are two parts that I have commented it with // strange comments which means that I have no idea is it where the error occur or something like that.

Thanks for reading my question.

This is my PetRescued Model

public class PetRescued
{
    public int Id { get; set; }

    [Required]
    [StringLength(255)]
    public string PetName { get; set; }

    public int PetAge { get; set; }

    [Required]
    [StringLength(6)]
    public string PetGender { get; set; }

    public short PetWeightInKg { get; set; }

    public DateTime DateWhenRescued { get; set; }

    public PetSpecies PetSpecies { get; set; }

    public byte PetSpeciesId { get; set; }

}

This is my PetRescued Controller

 public ActionResult New() //populate form
    {
        var petspecies = _context.PetSpecieses.ToList();

        var viewModel = new PetRescuedViewModel
        {
            PetSpecies = petspecies
        };

        return View("PetRescued", viewModel);
    }

[HttpPost]
public ActionResult Save(PetRescued petRescued)
{
    if (petRescued.Id == 0)
        _context.PetRescueds.Add(petRescued);
    else
    {
        var petRescuedInDb = _context.PetRescueds.Single(c => c.Id == petRescued.Id);
        petRescuedInDb.PetName = petRescued.PetName;
        petRescuedInDb.PetAge = petRescued.PetAge;
        petRescuedInDb.PetGender = petRescued.PetGender;
        petRescuedInDb.PetWeightInKg = petRescued.PetWeightInKg;
        petRescuedInDb.PetSpeciesId = petRescued.PetSpeciesId; //strange
        petRescuedInDb.DateWhenRescued = petRescued.DateWhenRescued;
     }

    _context.SaveChanges();
    return RedirectToAction("Index", "PetRescued");
}

This is my PetRescued ViewModel

public class PetRescuedViewModel
{
    public IEnumerable<PetSpecies> PetSpecies { get; set; }

    public PetRescued PetRescueds { get; set; }


    public PetRescuedViewModel()
    {
        PetRescueds = new PetRescued(); 
    }
}

This is my PetRescued Form

@using (Html.BeginForm("Save", "PetRescued"))
{
<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetName)
    @Html.TextBoxFor(m => m.PetRescueds.PetName, new { @class = "form-control" })
</div>

//strange
<div class="form-group">
    @Html.LabelFor(m => m.PetSpecies)
    @Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {@class = "form-control"})    
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetAge)
    @Html.TextBoxFor(m => m.PetRescueds.PetAge, new { @class = "form-control" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetGender)
    @Html.TextBoxFor(m => m.PetRescueds.PetGender, new { @class = "form-control" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetWeightInKg)
    @Html.TextBoxFor(m => m.PetRescueds.PetWeightInKg, new { @class = "form-control" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.DateWhenRescued)
    @Html.TextBoxFor(m => m.PetRescueds.DateWhenRescued, "{0:d MMM yyyy}", new { @class = "form-control" }) 
</div>

@Html.HiddenFor(m => m.PetRescueds.Id)
<button type="submit" class="btn btn-primary">Save</button>
}
Wan Choon
  • 25
  • 5
  • I don't see your `//strange` comments. – Chris.ZA Oct 15 '18 at 15:48
  • They are in PetRescued Controller "petRescuedInDb.PetSpeciesId = petRescued.PetSpeciesId" and PetRescued Form "DropDownList" statement. – Wan Choon Oct 15 '18 at 15:50
  • Sorry. I totally missed that! – Chris.ZA Oct 15 '18 at 16:15
  • There are multiple questions and answers that cover the error you're getting. Have you reviewed any of them? For instance, [this question](https://stackoverflow.com/questions/5400530/validation-failed-for-one-or-more-entities-while-saving-changes-to-sql-server-da) has some answers that show how to see the actual validation errors, which is usually the most helpful change. – Tieson T. Oct 15 '18 at 20:59
  • The model in your view is `PetRescuedViewModel` therefore the parameter in the POST method must be `PetRescuedViewModel`, not `PetRescued` as I noted in your previous question (and again - view models DO NOT contain data models!) –  Oct 15 '18 at 21:16
  • @TiesonT. Most of the Q&A that I have reviewed are mostly using Try&Catch or using "-update-database", I have reviewed the link you that you provided, I would like to try but I don't have a DbContext in my project. – Wan Choon Oct 16 '18 at 07:54
  • @StephenMuecke I tried your way with my original codes but this line in controller '_context.PetRescueds.Add(petRescued);' shows an error, "petRescued" is in red, so I change it to "new PetRescued()" which still result in the same error. – Wan Choon Oct 16 '18 at 07:54

3 Answers3

0

Let's try and fix this.

First, let's change your controller to be able to do something with the errors returned by the model binder.

[HttpGet]
public ActionResult New() //populate form
{
    var petspecies = _context.PetSpecieses.ToList();

    var viewModel = new PetRescuedViewModel
    {
        PetSpecies = petspecies
    };

    return View("PetRescued", viewModel);
}

[HttpPost]
public ActionResult Save(PetRescuedViewModel viewModel)
{
    if (ModelState.IsValid) // Check for errors
    {
        if (petRescued.Id == 0)
            _context.PetRescueds.Add(petRescued);
        else
        {
            var petRescuedInDb = _context.PetRescueds.Single(c => c.Id == petRescued.Id);
            petRescuedInDb.PetName = viewModel.PetRescued.PetName;
            petRescuedInDb.PetAge = viewModel.PetRescued.PetAge;
            petRescuedInDb.PetGender = viewModel.PetRescued.PetGender;
            petRescuedInDb.PetWeightInKg = viewModel.PetRescued.PetWeightInKg;
            petRescuedInDb.PetSpeciesId = viewModel.PetRescued.PetSpeciesId; //strange
            petRescuedInDb.DateWhenRescued = viewModel.PetRescued.DateWhenRescued;
        }

        _context.SaveChanges();
        return RedirectToAction("Index", "PetRescued");
    }

    viewModel.PetSpecies = _context.PetSpecieses.ToList();  // populate the list again as the contents are lost when the form is submitted.

    return View("PetRescued", viewModel); // validation errors found, so redisplay the same view
}

Then, change your view to display the errors. We're basically doing what this answer suggests.

@using (Html.BeginForm("Save", "PetRescued"))
{
    // Displays a summary of all the errors.
    @Html.ValidationSummary() 

    <div class="form-group">
        @Html.LabelFor(m => m.PetRescueds.PetName)
        @Html.TextBoxFor(m => m.PetRescueds.PetName, new { @class = "form-control" })
        // Or you can add this to each property            
        @Html.ValidationMessageFor(m => m.PetRescueds.PetName) 
    </div>

    //strange
    <div class="form-group">
        @Html.LabelFor(m => m.PetSpecies)
        @Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {@class = "form-control"})    
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.PetRescueds.PetAge)
        @Html.TextBoxFor(m => m.PetRescueds.PetAge, new { @class = "form-control" })
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.PetRescueds.PetGender)
        @Html.TextBoxFor(m => m.PetRescueds.PetGender, new { @class = "form-control" })
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.PetRescueds.PetWeightInKg)
        @Html.TextBoxFor(m => m.PetRescueds.PetWeightInKg, new { @class = "form-control" })
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.PetRescueds.DateWhenRescued)
        @Html.TextBoxFor(m => m.PetRescueds.DateWhenRescued, "{0:d MMM yyyy}", new { @class = "form-control" }) 
    </div>

    @Html.HiddenFor(m => m.PetRescueds.Id)
    <button type="submit" class="btn btn-primary">Save</button>
}

The above changes will at least give you which properties are having the problem.

The next step would be to fix the actual problem. If you do the above and can't figure it out further let me know which properties it is and I'll take a look.

I'm guessing it is public byte PetSpeciesId { get; set; } but let's see.

Hope this helps.

Chris.ZA
  • 1,218
  • 1
  • 15
  • 30
  • I tried your way but I'm getting this error "Value cannot be null. Parameter name: items" and the error shows Line 25 is the cause which is Line 25: @Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new { @class = "form-control" }). Your first step of solution which is "return View(viewModel);" is showing me that i don't have a viewModel so I created one myself which is var viewModel = new PetRescuedViewModel { PetRescueds = petRescued }; – Wan Choon Oct 15 '18 at 16:28
  • I have changed public int PetSpeciesId { get; set; } to byte data type but still error occurs – Wan Choon Oct 15 '18 at 17:56
  • Hello. I have updated my answer to include the viewmodel. You must have controller action that generates the form. How are you populating `public IEnumerable PetSpecies { get; set; }`? – Chris.ZA Oct 15 '18 at 19:05
  • I do have a controller tat generates the form, I have updated it in my question. I have used "add-migration" to populate the PetSpecies. 1 question sir, I don't understand what do you mean for this line `viewModel.PetSpecies = _someMethod.ToPopulate(); // populate the list` Populate what list ? – Wan Choon Oct 16 '18 at 08:00
  • I have updated my answer after you added your get method. Basically need to repopulate the `viewModel.PetSpecies` List (it is an `IEnumerable`) when re-displaying the form as the contents are lost when the form is posted. – Chris.ZA Oct 16 '18 at 08:16
  • `if (petRescued.Id == 0) _context.PetRescueds.Add(petRescued);` This line of code is in red so i change to viewModel and Intellisense shows me that I am only able to use `viewModel.PetRescueds.Id` so i make some changes similar to this. This time I am getting an error of **Cannot insert explicit value for identity column in table 'PetRescueds' when IDENTITY_INSERT is set to OFF.** I search for it and getting me a result of using `[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]` for my PK, then it shows me a new error which ApplicationDbContext model is changed – Wan Choon Oct 16 '18 at 08:29
  • And suggested me to update the database, I didn't update the database yet and revert the changes which is `[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]` – Wan Choon Oct 16 '18 at 08:30
0

Look at your model definition

// This means this value is required 
// and should not be greater than 255 characters 
[Required]
[StringLength(255)]
public string PetName { get; set; }

// This means this value is required 
// and should not be greater than 6 characters 
[Required]
[StringLength(6)]
public string PetGender { get; set; }

So either you are not sending a value from your client app or it is larger than the restrictions you stated. Change your action method to this to validate your model in your backend (you should never trust the client input)

[HttpPost]
public ActionResult Save(PetRescued petRescued)
{
    if (ModelState.IsValid) // Check for errors
    {
        if (petRescued.Id == 0)
            _context.PetRescueds.Add(petRescued);
        else
        {
            var petRescuedInDb = _context.PetRescueds.Single(c => c.Id == petRescued.Id);
            petRescuedInDb.PetName = petRescued.PetName;
            petRescuedInDb.PetAge = petRescued.PetAge;
            petRescuedInDb.PetGender = petRescued.PetGender;
            petRescuedInDb.PetWeightInKg = petRescued.PetWeightInKg;
            petRescuedInDb.PetSpeciesId = petRescued.PetSpeciesId; //strange
            petRescuedInDb.DateWhenRescued = petRescued.DateWhenRescued;
         }

        _context.SaveChanges();
        return RedirectToAction("Index", "PetRescued");
    }
    else
        return View(petRescued); // Return the same view with the original data
                                 // or with the correct model of your view, at least
}

UPDATE

Correct your view model to reflect your correct data. That means, make sure you are sending the correct model to the backend. ASP.Net MVC has something called Model Binding, which is the mechanism used to convert the data received from the client into your C# model. By default, it works by detecting the name of the values passed from the client and finding an exact mapping with the properties of the model. That means that in your view you are declaring this

    @Html.TextBoxFor(m => m.PetRescueds.PetName, new { @class = "form-control" })

So, if you inspect the data sent by the browser you will see that the form data includes something like

PetRescueds.PetAge: whatever_the_client_typed

That will not be mapped to your model, because your model doesn't have a property named PetRescueds with a subproperty named PetName, your action model is directly a PetRescued model. So either change your view by specifying directly the name attr like this

    @Html.TextBox("PetName", Model.PetRescueds.PetName, new { @class = "form-control" })

Or change your action model to reflect your view model definition. Either way, your view model should be consistent through your action and view. Otherwise, you will end up receiving null values in your action model in spite of filling them correctly on your view, or showing empty values in your views regardless of what you actually created on your controller action.

So, basically, check your model definitions. Make sure you are using a correct model definition to display in your views. Make sure your view is correctly defined as to what you are expecting to receive in your backend controller.

Then, change your view to include validation errors retrieved from the server

@using (Html.BeginForm("Save", "PetRescued"))
{
<!-- This will show your errors-->
@Html.ValidationSummary()
<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetName)
    <!-- Or you can show errors for each model property -->
    <!-- like this -->
    @Html.ValidationMessageFor(m => m.PetRescueds.PetName);
    @Html.TextBox("PetName", Model.PetRescueds.PetName, new { @class = "form-control" })
</div>

//strange
<div class="form-group">
    @Html.LabelFor(m => m.PetSpecies)
    @Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {@class = "form-control"})    
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetAge)
    @Html.TextBoxFor(m => m.PetRescueds.PetAge, new { @class = "form-control" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetGender)
    @Html.TextBoxFor(m => m.PetRescueds.PetGender, new { @class = "form-control" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.PetWeightInKg)
    @Html.TextBoxFor(m => m.PetRescueds.PetWeightInKg, new { @class = "form-control" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.PetRescueds.DateWhenRescued)
    @Html.TextBoxFor(m => m.PetRescueds.DateWhenRescued, "{0:d MMM yyyy}", new { @class = "form-control" }) 
</div>

@Html.HiddenFor(m => m.PetRescueds.Id)
<button type="submit" class="btn btn-primary">Save</button>
}

You can read more about data validation at Microsofts's

Alfredo A.
  • 1,697
  • 3
  • 30
  • 43
  • I tried your way but there is something wrong with this line of code `@Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {@class = "form-control"})` I highly doubt this is root of causes but I am clueless on how to fix this. – Wan Choon Oct 16 '18 at 08:13
  • I just copied your code. How are you populating your model? You should first verify your model creation before passing it to your view. So, the steps are: - Verify your model (make sure you are passing the correct data from your controller to your view) - Verify your view definition (make sure your HTML components are correctly defined according to your model needs) - Verify your model data validation (make sure the data returned from the client, e.g. the data received from the browser after the client filled your view, is correct according to your specs. This was your original question. – Alfredo A. Oct 16 '18 at 15:21
  • You have several problems in your original question. Your view is not sending the correct model (you are declaring the wrong model for your view, it is different from the model expected by the controller). Your Controller is not validating data received from the client. Your View is not showing validation errors. I updated the answer to show all those aspects. – Alfredo A. Oct 16 '18 at 17:48
  • Thanks for pointing out the problems, I'll check on the problems one by one but currently I am struggling on how to write a statement for the DropDownList in te view, the "??" part keeps getting error even when I tried to use IEnumerable `@Html.DropDownList("Pet Species", ?? , new SelectList (Model.PetSpecieses, "Id", "Name"), "Select Species Type", new { @class = "form-control" })` – Wan Choon Oct 16 '18 at 18:09
  • Ok, that's a different problem. I would advise you to verify the answers provided by all the users. My opinion is that you should mark your question as solved, since, by reading all the answers, the solution to your original question was provided, and ask another different question, to keep this specific thread focused on your original question. – Alfredo A. Oct 17 '18 at 13:48
0

You should use the try and catch method to see which fields cause the 'EntityValidationErrors' :

ActionResult Save =>

    try
    {
        _context.SaveChanges();;
    }
    catch (DbEntityValidationException ex)
    {
        var sb = new StringBuilder();

        foreach (var failure in ex.EntityValidationErrors)
        {
            sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
            foreach (var error in failure.ValidationErrors)
            {
                sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                sb.AppendLine();
            }
        }

        throw new DbEntityValidationException(
            "Entity Validation Failed - errors follow:\n" +
            sb.ToString(), ex
            );
    }

You will know then which records do the exception.

Yanga
  • 2,885
  • 1
  • 29
  • 32
  • I had remove everything in my Save() and try your Try&Catch, the errors I'm getting is **Object reference not set to an instance of an object.** and this line `Line 25: @Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new { @class = "form-control" })` is in red so i guess something is wrong with this but I can't figure out where ... – Wan Choon Oct 16 '18 at 08:36
  • I tried again with my codes and your try&catch, I spotted there is an error of this [DbEntityValidationException: Entity Validation Failed - errors follow: FWPaws6.Models.PetRescued failed validation - PetName : The PetName field is required. - PetGender : The PetGender field is required. ] But i do have input the Name and Gender before submitting the form .. – Wan Choon Oct 16 '18 at 08:48
  • 1
    Did you declare the model at the top of your view = > `@model mynamespace.Models.RegisterViewModel` ? If no maybe you can try to setup your input `m.PetRescueds.PetName` with `m.PetName` etc... Look like your form doesn't transmit the good model. – Yanga Oct 16 '18 at 11:41
  • Yes I do declare it – Wan Choon Oct 16 '18 at 12:23