Hi I have this piece of code that works when updating data into db.
However i have 20 columns in my table. how can i make sure:
1) Only ACCOUNT_ID will be updated? (currently all 17 columns get overwritten into NULL after updating)
2) how to indicate in UI that only ACCOUNT_ID is editable? (eg greyed out)
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MovieApp.Models;
namespace MovieApp.Controllers
{
public class HomeController : Controller
{
private dtsdbEntities _db = new dtsdbEntities();
// GET: Home
public ActionResult Index()
{
return View(_db.IPR_CompanyGen_200200501.ToList());
}
// GET: Home/Edit/5
public ActionResult Edit(int id)
{
var CompanyToEdit = (from m in _db.IPR_CompanyGen_200200501 where
(m.CompanyID.Equals(id.ToString())) select m);
return View(CompanyToEdit);
}
// GET: Home/Edit/5
[HttpPost]
public ActionResult Edit(IPR_CompanyGen_200200501 CompanyToEdit)
{
var OriginalCompany = (from m in _db.IPR_CompanyGen_200200501 where
(m.CompanyID.Equals(CompanyToEdit.CompanyID.ToString())) select m.First());
_db.Entry(OriginalCompany).CurrentValues.SetValues(CompanyToEdit).First();
_db.SaveChanges();
return RedirectToAction("Index");
}
}
internal class MoviesDBEntities
{
}
}
Edit.cshtml
#model MovieApp.Models.IPR_CompanyGen_200200501
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>IPR_CompanyGen_200200501</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.CompanyID)
<div class="form-group">
#Html.LabelFor(model => model.CompanyName, htmlAttributes: new { #class = "control-label
col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CompanyName, new { htmlAttributes = new { #class =
"form-control" } })
#Html.ValidationMessageFor(model => model.CompanyName, "", new { #class = "text-
danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ACCOUNT_ID, htmlAttributes: new { #class = "control-label
col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ACCOUNT_ID, new { htmlAttributes = new { #class =
"form-control" } })
#Html.ValidationMessageFor(model => model.ACCOUNT_ID, "", new { #class = "text-
danger" })
</div>
</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>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Tried with Bind but still columns goes to NULL (take CompanyName , Address1 as example)
[HttpPost]
public ActionResult Edit([Bind(Include = "CompanyID,CompanyName,Address1,Address2,Address3,City,StateProvinceCode,PostalCode,CountryCode,Region,PhoneNumber,URL,PrimaryCompanyType, DominantCompanyStyle, DominantCompanyOrientation, Buy_side, Sell_side, Strategic, EqPortfolioTurnover, ReportedEquityAssets, Status, LastModifiedDate, ACCOUNT_ID, CRM_Comments")] IPR_CompanyGen_200200501 CompanyToEdit)
{
if (ModelState.IsValid) {
var OriginalCompany = (from m in _db.IPR_CompanyGen_200200501 where (m.CompanyID.Equals(CompanyToEdit.CompanyID.ToString())) select m).First();
OriginalCompany.CompanyName = CompanyToEdit.CompanyName;
OriginalCompany.Address1 = CompanyToEdit.Address1;
_db.Entry(OriginalCompany).CurrentValues.SetValues(CompanyToEdit);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
This is your problem with your code:
OriginalCompany.CompanyName = CompanyToEdit.CompanyName;
OriginalCompany.Address1 = CompanyToEdit.Address1;
_db.Entry(OriginalCompany).CurrentValues.SetValues(CompanyToEdit);
You get the originalCompany from db assing new values to it and finally you edit the object which came as parameter which have all other properties null.
Replace it with this:
_db.Entry(OriginalCompany).CurrentValues.SetValues(OriginalCompany);
On the values that should not be touched at all, in the view, on the class="form-control" add a disabled attribute. Like
class="form-control" disabled
Also to prevent the values from being set NULL, put a Bind() in the [HttpPost] part of the controller. Like so
[HttpPost]
public ActionResult Edit([Bind(Include ="Id,Account_Id,NextField,OtherField,etc.")] IPR_CompanyGen_200200501 CompanyToEdit)
Inside of the [Bind("")], just put the column names that exist in the model. In the controller have if(ModelState.IsValid)
public ActionResult Edit(int id)
{
var CompanyToEdit = (from m in _db.IPR_CompanyGen_200200501 where
(m.CompanyID.Equals(id.ToString())) select m);
return View(CompanyToEdit);
}
// GET: Home/Edit/5
[HttpPost]
public ActionResult Edit([Bind(Include="Id,Company_Id,Account_Id,Other_Properties")] IPR_CompanyGen_200200501 CompanyToEdit)
{
if (ModelState.IsValid) {
var OriginalCompany = (from m in _db.IPR_CompanyGen_200200501 where
(m.CompanyID.Equals(CompanyToEdit.CompanyID.ToString())) select m.First());
OriginalCompany.CompanyName = CompanyToEdit.CompanyName;
OriginalCompany.Company_ID = CompanyToEdit.Company_ID;
OriginalCompany.WhatEverProperty = CompanyToEdit.WhatEverProperty;
OriginalCompany.Account_ID = CompanyToEdit.Account_ID;
//do this for every property and place a breakpoint to view every value
_db.Entry(OriginalCompany).CurrentValues.SetValues(CompanyToEdit).First();
_db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
Related
I am not sure how this works.
code for View is :
#model ReservationSys.Models.Confirmed
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Confirmed</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.RoomNumber, "RoomNumber", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RoomNumber", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.RoomNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ReservationId, "ReservationId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ReservationId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ReservationId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Reservation.Customer.CustomerName, "CustomerName", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CustomerName", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Reservation.Customer.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Similarly, code in controller to create entry in database is :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,RoomNumber,ReservationId")] Confirmed confirmed)
{
if (ModelState.IsValid)
{
db.Confirmeds.Add(confirmed);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ReservationId = new SelectList(db.Reservations, "ReservationId", "ReservationId", confirmed.ReservationId);
ViewBag.RoomNumber = new SelectList(db.Rooms, "RoomNumber", "RoomType", confirmed.RoomNumber);
return View(confirmed);
}
Can someone help me here to understand, how the selected value of dropdown is passed ?
I want to find reservation Id by using Customer Name for which I have created function like :
// get customer id using customer name that comes from dropdown list
public Customer GetCustomerId(string name)
{
Customer Customer = new Customer();
Customer=db.Customers.Find(name);
return Customer;
}
// use customer Id to get Reservation ID to enter in Reservation table of database
public Reservation getReservationId(int id)
{
Reservation reservation = db.Reservations.Find(id);
return reservation;
}
thanks in advance.
how the selected value of dropdown is passed ?
You can use asp-items,to new SelectList,it will show it's text and bind it's value to the property you want by ModelBinding.
Below is a work demo, you can refer to it.
Custom.cs:
public class Custom
{
public string name{ get; set; }
public int Id { get; set; }
}
CustomViewModel.cs:
public class CustomViewModel
{
public string name { get; set; }
public List<Custom> Customlist { get; set; }
}
In HomeContoller:
public IActionResult Index()
{
var Customlist = new List<Custom>()
{
new Custom { name = "Khushbu",Id= 1 },
new Custom { name = "Mohan", Id = 2 },
new Custom { name = "John", Id = 3 },
new Custom { name = "Martin", Id= 4 },
new Custom { name = "Due", Id= 5 }
};
var model = new CustomViewModel();
model.Customlist = Customlist;
return View(model);
}
[HttpPost]
public IActionResult GetCustomerId(string name)
{
return View();
}
If you are getting data from the database, then you can bind it like below,
model.Customlist= db.Select(x => new Custom { Id = x.Id, Text = x.name }).ToList();
Index view:
#model CustomViewModel
#{
ViewData["Title"] = " List";
}
<div class="text-center">
<h1 class="display-4">Custom Dropdown</h1>
<form asp-action="GetCustomerId" asp-controller="home" method="post">
<div class="row">
<div class="col-md-4">
<select id="drpEmpList" class="form-control" asp-for="name" asp-items="#(new SelectList(Model.Customlist, "name","name" ))">
<option value="">--Select--</option>
</select>
</div>
<div class="col-md-2">
<input type="submit" name="btnSubmit" value="Submit" class="btn btn-success" />
</div>
</div>
</form>
</div>
Result:
I am using ASP.Net MVC, I am trying to get values using a ViewModel.
FAQ class have (Name,Description)
FAQ_Detail class is List of Question and Answer which I am passing as PartialView
I have attached front end page and controller page, in controller I can get the Name,Description but always return NULL FAQ_Detail property
View Model
public class FAQViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IList<Details> FAQ_Details { get; set; }
}
public class Details
{
public int Id { get; set; }
public int FAQId { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FAQ</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<br />
<br />
<br />
#for (int i = 0; i <= 2; i++)
{
var m = new FAQ.Models.ViewModels.Details()
{
Id = i,
FAQId = 11,
Question = string.Format("Quesiton {0}",i),
Answer = string.Format("Ans. {0}",i)
};
#Html.Partial("_AddFAQ",m)
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Controller
[HttpPost]
public ActionResult Create(Models.ViewModels.FAQViewModel model) //The model.FAQ_Details array always empty
{
try
{
//
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The problem is you are just displaying the question/answer pair as form fields and there is nothing to bind them to the array in the model you expect. Usually, the name HTML attribute maps the client-side field to the property name of the model on the server.
The better thing you can do is to have some JavaScript, collect the data and pass that through an AJAX call to the server.
I found a solution, I pass master model in controller and use Detail model as collection
public ActionResult Create(string name,string description, ICollection<Models.ViewModels.Details> data)
{
try
{
//
return RedirectToAction("Index");
}
catch
{
return View();
}
}
I have a problem here with regards to elements on my pages returning null even though I have typed something in the textbox. What causes this? I want to make a simple CRUD app with a dashboard for final year.
Here is my view:
#model WebApplication1.Models.Category
#{
ViewBag.Title = "Create Category";
}
<h2>#ViewBag.Title</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class
="control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Name, new { htmlAttributes =
new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new {
#class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Here is my controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Name")] Category category)
{
if (ModelState.IsValid)
{
db.Categories.Add(category);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(category);
}
I think you need to post to the correct ActionName. You use #using (Html.BeginForm()), which will post to the Index of a Controller. But you have Create. So point the form to that.
#using (Html.BeginForm("Create", "Home", FormMethod.Post))
Make sure that you have proper viewmodel properties setup first:
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
}
Then point to action name and controller name which handles POST action in BeginForm helper:
#* assumed the controller name is 'CategoryController' *#
#using (Html.BeginForm("Create", "Category", FormMethod.Post))
{
// form contents
}
And finally change parameter name to avoid naming conflict in default model binder, also remove BindAttribute because the POST action has strongly-typed viewmodel class as parameter:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Category model)
{
if (ModelState.IsValid)
{
db.Categories.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
Related issue:
POST action passing null ViewModel
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>
}
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
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.
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.
I am following this blog Editing a variable length list, ASP.NET MVC 2-style by Steven Anderson and started using Html.BeginCollectionItem() for my ASP.NET MVC project.
The project is about associating multiple permissions to roles. It will have a Role page(view) where user will have option to add permissions by selecting one of the permissions from drop-down list. User will also be able to add these permissions drop-down list by clicking on 'Add' button. 'Add' would dynamically add new drop-down list control on the page. Remove would remove the last drop-down list.
The issue I am facing is with binding nested collection. I have used RolePermissions list below which inturn is a list of RolePermissionViewModel.
See below for screenshots.
Role Controller: I have hardcoded sample data for testing.
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web.Mvc;
using MUtilities.Model.Entities;
using MUtilities.Web.Areas.Admin.Models;
using MUtilities.Web.Controllers;
using HtmlHelpers.BeginCollectionItem;
namespace MUtilities.Web.Areas.Admin.Controllers
{
public class RoleController : BaseController
{
public ActionResult Index()
{
return View();
}
public ViewResult BlankPermission()
{
return View("_PermissionPartial", GetPermissions());
}
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var role = MRepository.AllRoles.Find(id);
if (role == null)
{
return HttpNotFound();
}
return View(role);
}
private List<List<RolePermissionViewModel>> GetPermissionList() {
var rolePermissions = new List<List<RolePermissionViewModel>>();
rolePermissions.Add(GetPermissions());
rolePermissions.Add(GetPermissions());
return rolePermissions;
}
private List<RolePermissionViewModel> GetPermissions()
{
var permissions = new List<RolePermissionViewModel>();
permissions.Add(new RolePermissionViewModel
{
PermissionId = -1,
PermissionName = "--Select a Permission--"
});
permissions.Add(new RolePermissionViewModel
{
PermissionId = 1,
PermissionName = "Create of Utility1"
});
permissions.Add(new RolePermissionViewModel
{
PermissionId = 2,
PermissionName = "Update of of Utility1"
});
return permissions;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,Description,RolePermissions")] RoleViewModel roleModel)
{
if (ModelState.IsValid)
{
var role = new Role();
role.Name = roleModel.Name;
//some logic here
return RedirectToAction("Index");
}
return View();
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//some logic to edit view
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Name,Permissions")] RoleViewModel roleModel)
{
if (ModelState.IsValid)
{
var role = MRepository.AllRoles.First(r => r.Id == roleModel.Id);
role.Name = roleModel.Name;
//some logic to commit
return RedirectToAction("Index");
}
return View();
}
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Role role = MRepository.AllRoles.Find(id);
if (role == null)
{
return HttpNotFound();
}
return View(role);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
var role = MRepository.AllRoles.Find(id);
MRepositoryAllRoles.Remove(role);
MRepository.Commit();
return RedirectToAction("Index");
}
}
}
Role View:
#model MUtilities.Web.Areas.Admin.Models.RoleViewModel
#{
ViewBag.Title = "Create";
}
#section scripts{
<script type="text/javascript">
$(function () {
$("#addPermissionBtn").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) {
$("#addPermissionDdl").append(html);
//The call to Sys.Mvc.FormContext._Application_Load() refreshes
//the validation on the page to include the new fields.
Sys.Mvc.FormContext._Application_Load();
}
});
return false;
});
$("#deletePermissionBtn").on("click", function () {
var pDdls = $("select[id$='__PermissionList']");
if (pDdls && pDdls.length > 1) {
pDdls.last().remove();
//$(this).parents("div.addPermissionDdl:last").remove();
}
return false;
});
});
</script>
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Role</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RolePermissions, htmlAttributes: new { #class = "control-label col-md-2" })
#using (Html.BeginForm())
{
<div id="addPermissionDdl" class="col-md-10">
#foreach (var rolePermission in Model.RolePermissions)
{
Html.RenderPartial("_PermissionPartial", rolePermission);
}
</div>
<div class="col-md-10">
#Html.ActionLink("Add", "BlankPermission", null, new { id = "addPermissionBtn", #class = "glyphicon glyphicon-plus-sign" })
Delete
</div>
}
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
RoleViewModel: RolePermissions is a list of RolePermissionViewModel list.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MUtilities.Web.Areas.Admin.Models
{
public class RoleViewModel
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Display(Name="Permissions")]
public List<List<RolePermissionViewModel>> RolePermissions { get; set; }
}
public class RolePermissionViewModel
{
public int PermissionId { get; set; }
public string PermissionName { get; set; }
}
}
_PermissionPartial.cshtml
#{
Layout = null;
}
#using HtmlHelpers.BeginCollectionItem;
#model IEnumerable<MUtilities.Web.Areas.Admin.Models.RolePermissionViewModel>
#using (Html.BeginCollectionItem("RolePermissions"))
{
var permissionDdl = new SelectList(Model.Select(m => m), "PermissionId", "PermissionName", "--Select a Permission--");
#Html.DropDownList("PermissionList", permissionDdl, new { #class = "form-control" });
}
But when I click Create button Name, Description would bind ok but not the Permissions. See below.
Any idea what might be happening?