jQuery and ASP.NEt MVC validation: Prevent validation on server - jquery-validate

I have model that looks like this:
public class Lesson
{
public int Id { get; set; }
[Required(ErrorMessage = "Naam voor de les is verplicht")]
[Display(Name="Naam")]
public string Name { get; set; }
[Required(ErrorMessage = "Tijd is verplicht")]
[Display(Name="Tijd")]
public string Time { get; set; }
//Prijs is default exclusief BTW
[Required(ErrorMessage = "Prijs is verplicht")]
[Display(Name="Prijs (excl btw)")]
public double Price { get; set; }
[Display(Name = "Maximum aantal leerlingen")]
public int MaxStudents { get; set; }
}
And a create view that looks like this:
<div>
<div>
#Html.LabelFor(model => model.Name)
</div>
<div>
#Html.TextBoxFor(model => model.Name, new { #class = "gt-form-text" })
#Html.ValidationMessageFor(model => model.Name)
</div>
<div>
#Html.LabelFor(model => model.Time)
</div>
<div>
#Html.TextBoxFor(model => model.Time, new { #class = "gt-form-text" })
#Html.ValidationMessageFor(model => model.Time)
</div>
<div>
#Html.LabelFor(model => model.Price)
</div>
<div>
#Html.TextBoxFor(model => model.Price, new { #class = "gt-form-text" })
#Html.ValidationMessageFor(model => model.Price)
</div>
<div>
#Html.LabelFor(model => model.MaxStudents)
</div>
<div>
#Html.TextBoxFor(model => model.MaxStudents,
new { #class = "gt-form-text" })
#Html.ValidationMessageFor(model => model.MaxStudents)
</div>
</div>
In the Application_Start() method I set DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes to false.
MaxStudents is nullable and not required, nevertheless, if MaxStudents is null, the Modelstate is not valid (ModelState.IsValid = false).
My errormessage is not shown, instead of that I get the errormessage is A value is required.
What can I do to get ModelState.IsValid to be true?

Someone shared this with me, and I'll pass it on in case it helps. Sometimes those validation errors get swallowed up and are hard to find. When I was having a similar issue, I added this code to my ActionResult (HttpPost) and it got me right to the error in question.
try
{
db.Entry(myModel).State = EntityState.Modified;
db.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
} // }

Related

How to make Optimistic Concurrency editing in Asp.net mvc 3 without using hiddenfields in the view?

I'm studying asp.net mvc 3 right now and I'm following this tutorial Contoso University
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application
I'm in this part where editing a model is handled with Optimistic Concurrency
I'm aware that by using something like
[HttpPost]
public ActionResult Edit(Department department)
the model will automatically be binded, even without a hidden field for the id of the department to be edited, and the edit will not fail.
But whenever I try to remove the two hiddenfields in the view of this one:
#model MvcContosoUniversity.Models.Department
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Department</legend>
#Html.HiddenFor(model => model.DepartmentID)
#Html.HiddenFor(model => model.Timestamp)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Budget)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Budget)
#Html.ValidationMessageFor(model => model.Budget)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.StartDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StartDate)
#Html.ValidationMessageFor(model => model.StartDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InstructorID, "Administrator")
</div>
<div class="editor-field">
#Html.DropDownList("InstructorID", String.Empty)
#Html.ValidationMessageFor(model => model.InstructorID)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
I get an error in the controller, here's the code for the controller:
// GET: /Department/Edit/5
public ActionResult Edit(int id)
{
Department department = db.Departments.Find(id);
ViewBag.InstructorID = new SelectList(db.Instructors, "InstructorID", "FullName", department.InstructorID);
return View(department);
}
//
// POST: /Department/Edit/5
[HttpPost]
public ActionResult Edit(Department department)
{
try
{
if (ModelState.IsValid)
{
db.Entry(department).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
//Another option is to put the try-catch inside a function
try
{
var databaseValues = (Department)entry.GetDatabaseValues().ToObject();
var clientValues = (Department)entry.Entity;
if (databaseValues.Name != clientValues.Name)
ModelState.AddModelError("Name", "Current value: "
+ databaseValues.Name);
if (databaseValues.Budget != clientValues.Budget)
ModelState.AddModelError("Budget", "Current value: "
+ String.Format("{0:c}", databaseValues.Budget));
if (databaseValues.StartDate != clientValues.StartDate)
ModelState.AddModelError("StartDate", "Current value: "
+ String.Format("{0:d}", databaseValues.StartDate));
if (databaseValues.InstructorID != clientValues.InstructorID)
ModelState.AddModelError("InstructorID", "Current value: "
+ db.Instructors.Find(databaseValues.InstructorID).FullName);
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
department.Timestamp = databaseValues.Timestamp;
}
catch(NullReferenceException e)
{
ModelState.AddModelError("","Error \n "+e.Message);
}
}
catch (DataException)
{
//Log the error (add a variable name after Exception)
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists contact your system administrator.");
}
ViewBag.InstructorID = new SelectList(db.Instructors, "InstructorID", "FullName", department.InstructorID);
return View(department);
}
Here is the code for the model:
public class Department
{
public int DepartmentID { get; set; }
[Required(ErrorMessage = "Department name is required.")]
[MaxLength(50)]
public string Name { get; set; }
[DisplayFormat(DataFormatString = "{0:c}")]
[Required(ErrorMessage = "Budget is required.")]
[Column(TypeName = "money")]
public decimal? Budget { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Required(ErrorMessage = "Start date is required.")]
public DateTime StartDate { get; set; }
[Display(Name = "Administrator")]
public int? InstructorID { get; set; }
public virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
[Timestamp]
public Byte[] Timestamp { get; set; }
}
Is it possible to make it work without using hiddenfields?
Sir/Ma'am your answers would be of great help. Thank you++
You need to have the id value coming from somewhere, or that entity can't be saved... how does EF know what ID it's saving without it?
It doesn't have to be via hidden field; It's probably there in the query string from the initial GET request for the edit action.

complex model and unique id for each item of list properties in mvc

I am developing an asp.net mvc 3.0 and using complex model in a view as below :
#model StoresAndMalls.DataModel.Entities.User
........
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Status)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Status)
#Html.ValidationMessageFor(model => model.Status)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Role)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Role)
#Html.ValidationMessageFor(model => model.Role)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UserRules)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserRules,"UserRules", new { AllRules = ViewBag.AllRules})
</div>
Here are my models :
public partial class User
{
......
public virtual ICollection<UserRule> UserRules { get; set; }
public virtual Role Role { get; set; }
}
public class UserRule
{
.....
[ForeignKey("UserId")]
public virtual User User { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
}
public partial class Role
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In recent projects, when I created a view with complex model, the form elements, such as Role, where prefixed with User, I mean there were like 'User.Role', but now they are not, and even though I use editor template for 'UserRules' property of User class, it also does not work as well and do not have 'User' prefix with,
here are my editor views for Role and UserRule:
#model IEnumerable<StoresAndMalls.DataModel.Entities.UserRule>
#{
var allrules = (ViewData["AllRules"] as List<StoresAndMalls.DataModel.Entities.Rule>);
int c = 0;
}
#
foreach (var item in allrules)
{
#item.Description
#Html.CheckBox(Model.Any(x => x.RuleId == item.Id))
c++;
<br />
}
-----------
#model StoresAndMalls.DataModel.Entities.Role
#{
var selecetList = (ViewData["Roles"] as List<StoresAndMalls.DataModel.Entities.Role>).
ConvertAll(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name, Selected = x.Id == Model.Id });
}
#Html.DropDownListFor(x => x, selecetList)
-------------
For 'UserRule' I tried
#model StoresAndMalls.DataModel.Entities.UserRule
But it throws :
The model item passed into the dictionary is of type 'System.Collections.Generic.HashSet`1[DataModel.Entities.UserRule]', but this dictionary requires a model item of type 'DataModel.Entities.UserRule'.
Edited :
public ActionResult UpdateManager(Guid id)
{
ViewBag.Roles = unitofwork.RoleRepository.Get();
ViewBag.AllRules = unitofwork.UserRepository.GetByID(id).UserRules;
var model = unitofwork.UserRepository.GetByID(id);
return View(model);
}

MVC3 - passing null item when model requires TimeSpan

Well I've recently come up against an interesting problem that I can't seem to figure out.
The error message I get is:
{"The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type 'System.TimeSpan'."}
This occurs when I try to submit a new entry to the database. So, the details of what is being submitted.
The model class:
public class EventModel
{
[Key]
public int EventID { get; set; }
[DisplayName("Booking title")]
[Required(ErrorMessage="Please provide a title for the booking")]
public string Title { get; set; }
[DataType(DataType.Date)]
[DisplayName("Start date")]
[DisplayFormat(DataFormatString="{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime StartDateTime { get; set; }
[DisplayName("End date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
[IsDateAfter("StartDateTime", true, ErrorMessage="End date must be on or after the start date")]
public DateTime EndDateTime { get; set; }
public long StartTicks { get; set; }
public long EndTicks { get; set; }
[NotMapped]
[DisplayName("Start Time")]
public TimeSpan StartTime
{
get { return TimeSpan.FromTicks(StartTicks); }
set { StartTicks = value.Ticks; }
}
[NotMapped]
[DisplayName("End Time")]
public TimeSpan EndTime
{
get { return TimeSpan.FromTicks(EndTicks); }
set { EndTicks = value.Ticks; }
}
[DefaultValue(2)]
[DisplayName("Booking is")]
public int BookingStatus { get; set; }
[DisplayName("Set recurrence")]
[DefaultValue(false)]
public bool DoesRecur { get; set; }
[DisplayName("Set recurrence type")]
public string Pattern { get; set; }
[DisplayName("Set the day this happens on ")]
public int DayIndex { get; set; }
[DisplayName("Choose the day instance this recurs on")]
public int DayCount { get; set; }
[DisplayName("Day ")]
[NotMapped]
public string Day { get; set; }
[DisplayName("Instance")]
[NotMapped]
public string Instance { get; set; }
// links resource to a user/member
[DisplayName("Booked by")]
[NotMapped]
public string BookerName { get; set; }
public Guid MemberID { get; set; }
// links resource to a resource type
[DisplayName("Resource required:")]
public int ResourceID { get; set; }
}
The action methods in the controller class:
[HttpGet]
public ActionResult Create(DateTime eventDate)
{
var days = from DayOfWeek d in Enum.GetValues(typeof(DayOfWeek))
select new { ID = (int) d, Name = (DayOfWeek)d };
var instance = from DayInstance i in Enum.GetValues(typeof(DayInstance))
select new { ID = (int) i, Name = (DayInstance)i };
MembershipUser mu = Membership.GetUser(HttpContext.Profile.UserName);
CreateEventViewModel model = new CreateEventViewModel()
{
Event = new EventModel()
{
StartDateTime = eventDate,
EndDateTime = eventDate,
MemberID = (Guid)mu.ProviderUserKey
},
Resources = DBContext.Resources.ToList(),
Patterns = DBContext.Patterns.ToList(),
ResourceTypes = DBContext.ResourceTypes.ToList()
};
ViewData["dayOfWeek"] = new SelectList(days, "ID", "Name", DayOfWeek.Monday);
ViewData["dayInstance"] = new SelectList(instance, "ID", "Name", DayInstance.First);
return View(model);
}
[HttpPost]
public ActionResult Create(CreateEventViewModel em)
{
if (ModelState.IsValid)
{
// get the resource turn aournd time
double turnAround = rc.GetResourceTurnAround(em.Event.ResourceID);
MembershipUser mu = Membership.GetUser(HttpContext.Profile.UserName);
em.Event.MemberID = (Guid) mu.ProviderUserKey;
em.Event.BookingStatus = 2;
// need to get the time added to the date.
DateTime actualStartPoint = new DateTime(em.Event.StartDateTime.Ticks + em.Event.StartTicks);
DateTime actualEndPoint = new DateTime(em.Event.EndDateTime.Ticks + em.Event.EndTicks);
em.Event.StartDateTime = actualStartPoint;
em.Event.EndDateTime = actualEndPoint;
// add turn around time to the end of the event
em.Event.EndDateTime = em.Event.EndDateTime.AddMinutes(turnAround);
// needed becase these are handled slighty differently to the rest of the model
em.Event.DayIndex = int.Parse(Request.Form.GetValues("dayOfWeek").GetValue(0).ToString());
em.Event.DayCount = int.Parse(Request.Form.GetValues("dayInstance").GetValue(0).ToString());
DBContext.Events.Add(em.Event);
DBContext.SaveChanges();
// get the resource owner
MembershipUser resourceOwner = Membership.GetUser(rc.GetResourceOwnerByID(em.Event.ResourceID));
// email the admin team and the user the details of this booking
// get the email address of the user making the booking
StringBuilder message = new StringBuilder();
message.AppendFormat("Thank you for your booking, this is now being reviewed by the team.\nThe details of your booking are included for confirmation.\n");
message.AppendFormat("Booking Title: {0}\nResource: {1}\n Date: {2} {3} (this includes our turn around time added on)\n", em.Event.Title, rc.GetResourceNameByID(em.Event.ResourceID), actualStartPoint, actualEndPoint);
message.AppendFormat("You can log in at any time to review your bookings.\nYou will receive an email when the team have reviewed this request\nMany thanks\n");
EmailHandler eh = new EmailHandler();
eh.SetRecipient(Membership.GetUser().Email);
eh.AddAdminEmail();
eh.AddBcc(resourceOwner.Email);
eh.SetSubject("Booking Requested");
eh.SetBody(message.ToString());
eh.sendMessage();
return RedirectToAction("Index");
}
else
{
return View();
}
}
Now for the view items - the main view:
#model AssetManager.Models.CreateEventViewModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend id="bookingLegend">Place Booking</legend>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.Title)
#Html.ValidationMessageFor(model => model.Event.Title)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.StartDateTime)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.StartDateTime, new { #class = "date" })
#Html.ValidationMessageFor(model => model.Event.StartDateTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label timeSelector">
#Html.LabelFor(model => model.Event.StartTime)
</div>
<div class="editor-field timeSelector">
#Html.EditorFor(model => model.Event.StartTime)
#Html.ValidationMessageFor(model => model.Event.StartTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.EndDateTime)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.EndDateTime, new { #class = "date" })
#Html.ValidationMessageFor(model => model.Event.EndDateTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label timeSelector">
#Html.LabelFor(model => model.Event.EndTime)
</div>
<div class="editor-field timeSelector">
#Html.EditorFor(model => model.Event.EndTime)
#Html.ValidationMessageFor(model => model.Event.EndTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.Label("Select Resource Type")
</div>
<div class="editor-field">
#Html.DropDownList("ResourceTypes", new SelectList(Model.ResourceTypes, "ResourceTypeID", "Title"), "-- Select Resource Type --", new { #id = "ddlResourceTypes" })
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.ResourceID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Event.ResourceID, new SelectList(Enumerable.Empty<SelectListItem>(), "ResourceType", "Name"), "-- Select Resource --", new { #id = "ddlResources" })
#Html.ValidationMessageFor(model => model.Event.ResourceID)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.DoesRecur)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.DoesRecur)
#Html.ValidationMessageFor(model => model.Event.DoesRecur)
</div>
</div>
<div id="recurType" class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.Pattern)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Event.Pattern, new SelectList(Model.Patterns, "PatternCode", "Pattern"), "-- Select Recurrence Pattern --")
#Html.ValidationMessageFor(model => model.Event.Pattern)
</div>
</div>
<div id="recurDayHappens" class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.DayIndex)
</div>
<div class="editor-field">
#Html.DropDownList("dayOfWeek")
#Html.ValidationMessageFor(model => model.Event.DayIndex)
</div>
</div>
<div id="recurInstance" class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.DayCount)
</div>
<div class="editor-field">
#Html.DropDownList("dayInstance")
#Html.ValidationMessageFor(model => model.Event.DayCount)
</div>
</div>
<div class="controlcontainer">
<p>
<input class="subButton" type="submit" value="Create" />
<input id="cancelBtn" class="cancelButton" type="button" value="Cancel" onclick="location.href='#Url.Action("Index", "Calendar")'" />
</p>
</div>
</fieldset>
}
Then there is an editor template for the TimeSpan items:
#model TimeSpan
#Html.DropDownList("Hours", Enumerable.Range(0, 24)
.Select(i => new SelectListItem { Value = i.ToString(),
Text = i.ToString(), Selected = Model.Hours == i })) :
#Html.DropDownList("Minutes", Enumerable.Range(0, 60)
.Select(i => new SelectListItem { Value = i.ToString(),
Text = i.ToString(), Selected = Model.Minutes == i }))
And finally a TimeBinder class:
public class TimeBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Ensure there's incomming data
var key_hours = bindingContext.ModelName + ".Hours";
var valueProviderResult_hours = bindingContext.ValueProvider
.GetValue(key_hours);
var key_minutes = bindingContext.ModelName + ".Minutes";
var valueProviderResult_minutes = bindingContext.ValueProvider
.GetValue(key_minutes);
if (valueProviderResult_hours == null || string.IsNullOrEmpty(valueProviderResult_hours.AttemptedValue)
|| valueProviderResult_minutes == null || string.IsNullOrEmpty(valueProviderResult_minutes.AttemptedValue))
{
return null;
}
// Preserve it in case we need to redisplay the form
bindingContext.ModelState.SetModelValue(key_hours, valueProviderResult_hours);
bindingContext.ModelState.SetModelValue(key_minutes, valueProviderResult_minutes);
// Parse
var hours = ((string[])valueProviderResult_hours.RawValue)[0];
var minutes = ((string[])valueProviderResult_minutes.RawValue)[0];
// A TimeSpan represents the time elapsed since midnight
var time = new TimeSpan(Convert.ToInt32(hours), Convert.ToInt32(minutes), 0);
return time;
}
}
That's it, that is all the code that is involved. I am completely baffled as to why this error occurs. Any ideas or suggestions as to the cause and the solution are greatly appreciated.
Many thanks
nathj07
EDIT
Pk, so I tried something differnt with th TimeSpan editor template:
#model TimeSpan?
#Html.DropDownList("Hours", Enumerable.Range(0, 24)
.Select(i => new SelectListItem
{
Value = i.ToString(),
Text = i.ToString(),
Selected = Model.HasValue ? Model.Value.Hours == i : false
})) :
#Html.DropDownList("Minutes", Enumerable.Range(0, 60)
.Select(i => new SelectListItem
{
Value = i.ToString(),
Text = i.ToString(),
Selected = Model.HasValue ? Model.Value.Minutes == i : false
}))
This seems to have overcome this error but now I get an issue a little further down. In the view there is a DropDownList("ResourceTypes"....) This is essentially a dropdownlist that is used to control what appears in the DropDownListFor(model=>model.Event.ResourceID.....) There is a simple piece of JavaScript:
$(document).ready(function () {
$("#ddlResourceTypes").change(function () {
var idResourceType = $('#ddlResourceTypes').val();
$.getJSON("/Resource/LoadResourcesByType", { id: idResourceType },
function (resourceData) {
var select = $("#ddlResources");
select.empty();
select.append($('<option/>', {
value: 0,
text: "-- Select Resource --"
}));
$.each(resourceData, function (index, itemData) {
select.append($('<option/>', {
value: itemData.Value,
text: itemData.Text
}));
});
});
});
});
Now the issue I get is:
Object reference not set to an instance of an object
On the DropDownList("ResourceTypes".....)
Any ideas on this one?
When you POST an invalid form, you end with the code return View().
So you display the same view, without passing a model, the Model will be null. The first time your code really needs a value is in the editor for TimeSpan. That value is a nullable now, but you never test for the case that it is null.
Change the return to:
return View(em);
to pass the model, or use the code from the GET action, to rebuild and pass the model:
return Create(/* Your create date */);
Edit after comment
The error in the ModelBinder might be caused by the lines:
var hours = ((string[])valueProviderResult_hours.RawValue)[0];
var minutes = ((string[])valueProviderResult_minutes.RawValue)[0];
You convert an array to a string[]. I would do the conversion to string as late as possible, and make it more error proof by:
var hours = Convert.ToString(((object[])valueProviderResult_hours.RawValue).FirstOrDefault()) ?? "00";
This will just cast to an array of object, so there's less change to fail. Take the first element, or return null, and convert that to a string using Convert, and if the result is still null, return "00".
Hit breakpoints in your partial views and check the Model object, somewhere you will find a wrong object for a partial view. that is the cause of this error

How to update a domain object with ViewModel and AutoMapper using Entity framework?

How do i update a domain object with ViewModel with AutoMapper using Entity framework?
I have a View to edit a Question entity.
This is my Edit action:
public ActionResult Edit(int id)
{
var question = db.Question.Single(q => q.question_id == id);
Mapper.CreateMap<Question, EditQuestionViewModel>();
EditQuestionViewModel eqvm = Mapper.Map<Question, EditQuestionViewModel>(question);
eqvm.QuestionTypes = new SelectList(db.Question_Type, "type_code", "type_description", question.type_code);
eqvm.Categories = new SelectList(db.Category, "category_id", "category_name", question.category_id);
eqvm.Visibility = new SelectList(new Dictionary<int, string> {
{ 1, "Ja"},
{ 0, "Nej"}
}, "Key", "Value");
return View(eqvm);
}
And my ViewModel looks like this:
public class EditQuestionViewModel
{
public int question_id { get; set; }
public string question_wording { get; set; }
public bool visible { get; set; }
public int question_number { get; set; }
public string help_text { get; set; }
public Category Category { get; set; }
public Question_Type Question_Type { get; set; }
public string SelectedCategory { get; set; }
public string SelectedQuestionType { get; set; }
public SelectList Categories { get; set; }
public SelectList QuestionTypes { get; set; }
public SelectList Visibility { get; set; }
public string RefUrl { get; set; }
}
This is the View:
#using (Html.BeginForm("Edit", "AdminQuestion", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Spørgsmål</legend>
<div class="editor-label">
#Html.LabelFor(model => model.question_wording, "Spørgsmål")
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.question_wording, new { #class = "required", rows = 3, cols = 50 })
#Html.ValidationMessageFor(model => model.question_wording)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SelectedCategory, "Hvilken kategori tilhører dette spørgsmål?")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedCategory, Model.Categories)
#Html.ValidationMessageFor(model => model.SelectedCategory)
</div>
<div class="editor-label">
#Html.LabelFor(x => x.SelectedQuestionType, "Spørgsmålstype")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedQuestionType, Model.QuestionTypes)
#Html.ValidationMessageFor(model => model.SelectedQuestionType)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.visible, "Skal dette spørgsmål være synligt?")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.visible, Model.Visibility)
#Html.ValidationMessageFor(model => model.visible)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.question_number, "Hvilket nummer har spørgsmålet inden for sin kategori?")
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.question_number, new { #class = "required digits" })
#Html.ValidationMessageFor(model => model.question_number)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.help_text, "Hjælpetekst som hjælper brugeren med at forstå spørgsmålet:")
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.help_text, new { rows = 20, cols = 50 })
#Html.ValidationMessageFor(model => model.help_text)
</div>
<br />
<input type="submit" value="Gem" />
</fieldset>
How do i update the entity when i submit the form ?
How should the mapping between the ViewModel and EF Model look like, using AutoMapper?
The properties
public string SelectedCategory { get; set; }
public string SelectedQuestionType { get; set; }
In the ViewModel are supposed to be linked with category_id and type_code in the EF model
Also notice the property
public bool visible { get; set; }
I use BIT in my database. Will this work with the values 0 and 1, which is use in the SelectList?
Thanks!
you would need to get the object from entity framework, and then use automapper like this:
var item = repository.getbyid(model.Id);
_mappingEngine.Map(viewModel, item);
repository.save(item);
When you submit your form, you need to have an action on your controller that will handle the post to the server.
So in addition to the Edit action you currently have, you will need to have another action defined like so:
[HttpPost]
public ActionResult Edit(EditQuestionViewModel model)
{
//Do the mapping to from your ViewModel to the EF model here
return View();
}
What this does is sets up a handler so your form can post the data back to the controller and it will bind your fields on your form to the model parameter.
Once you have done this, you can simply map the model back to EF and persist it to the database.
Also, using a bool is perfectly valid and EF will translate and save it as a 0 or 1 in the database for you.

mvc3 razor dropdownlistfor causing update model to fail

I have a problem with the following code. Basically when I call updatemodel it fails but I dont get an inner exception. If i change the drop down to a text box it works.
class:
public class Member
{
[Key]
public int MemberID { get; set; }
[StringLength(50),Required()]
public string Name { get; set; }
[Range(0,120)]
public short Age { get; set; }
[Required(),MaxLength(1)]
public string Gender {get; set;}
[Required()]
public virtual Team Team { get; set; }
}
controller methods:
[HttpGet]
public ActionResult ViewMember(int id)
{
try
{
var m = db.GetMember(id);
var maleItem = new SelectListItem{Text = "Male", Value= "M", Selected=m.Gender == "M"};
var femaleItem = new SelectListItem{Text = "Female", Value="F", Selected=m.Gender == "F"};
List<SelectListItem> items = new List<SelectListItem>();
items.Add(maleItem);
items.Add(femaleItem);
var genders = new SelectList(items, "Value", "Text");
ViewBag.Genders = genders;
return View(m);
}
catch (Exception ex)
{
return View();
}
}
[HttpPost]
public ActionResult ViewMember(Member m)
{
try
{
var member = db.GetMember(m.MemberID);
m.Team = db.GetTeam(member.Team.TeamID);
UpdateModel(member);
db.Save();
return RedirectToAction("ViewMembers", new { id = member.Team.TeamID });
}
catch (Exception ex)
{
return View();
}
}
and finally cshtml code:
#model GreenpowerAdmin.Models.Member
#using GreenpowerAdmin.Helpers
#{
ViewBag.Title = "ViewMember";
}
<h2>ViewMember</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Member</legend>
#Html.HiddenFor(model => model.MemberID)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Team.Name)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Team.Name)
#Html.ValidationMessageFor(model => model.Team.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Gender)
</div>
<div class="editor-field">
#Html.DropDownList("Gender", ViewBag.Genders as SelectList)
#Html.ValidationMessageFor(model => model.Gender)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "ViewMembers", new { id=Model.Team.TeamID })
</div>
When I click the save button the updatemodel line fails. As said above, it doesn't fail if I use a text box for gender. Does anyone know what is making it fail or how to debug it?
Try declaring the MemberID property as a nullable integer:
[Key]
public int? MemberID { get; set; }

Resources