MVC3 - passing null item when model requires TimeSpan - asp.net-mvc-3

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

Related

Unable to retrieve the hidden element in partial view from main view

I have implemented kendo combobox on my MVC5 view and want the combobox to be filtered based on the value in my model. I need to retrieve the value from my model. I have currently bound that value to a hidden field called CountryCode in my partial view. The script is in my main view. I am getting error message undefined while trying to access the hidden field. The model is definitely getting populated with the CountryCode.
#using System.Collections
#model CC.GRP.MCRequest.ViewModels.RequestStatusUpdateViewModel
#{
Layout = null;
}
<div class="k-popup-edit-form k-window-content k-content" >
<div class="k-edit-form-container">
#Html.HiddenFor(model => model.CountryCode)
<div class="editor-label">
#Html.LabelFor(model => model.RequestID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RequestID, new { htmlAttributes = new { #readonly = "readonly" } })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ProjectName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProjectName, new { htmlAttributes = new { #readonly = "readonly" } })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RequestStatus)
</div>
<div class="editor-field">
#(Html.Kendo().ComboBoxFor(model => model.RequestStatusCode)
.HtmlAttributes(new { style = "width:100%" })
.DataTextField("Status")
.Placeholder("Select...")
.DataValueField("RequestStatusCode")
.AutoBind(false)
.Filter("contains")
.DataSource(dataSource => dataSource
.Read(read =>
{
read.Action("GetRequestStatus", "Request")
.Type(HttpVerbs.Post)
.Data("GetCountryCodeFilter");
}).ServerFiltering(true)
)
)
</div>
#Html.ValidationMessageFor(model => model.RequestStatusCode, "", new { #class = "text-danger" })
</div>
</div>
Controller method that populates the combo
public ActionResult GetRequestStatus(string countryCode)
{
var response = requestRepository.GetRequestStatus(countryCode).AsQueryable().ProjectTo<RequestStatusViewModel>();
var jsonResult = Json(response, JsonRequestBehavior.AllowGet);
jsonResult.MaxJsonLength = int.MaxValue;
return jsonResult;
}
Controller method that loads the view
public ActionResult RequestStatus(int requestId, string projectName, string countryCode)
{
RequestStatusUpdateViewModel reqeustStatusUpdateViewModel = new RequestStatusUpdateViewModel();
reqeustStatusUpdateViewModel.RequestID = requestId;
reqeustStatusUpdateViewModel.ProjectName = projectName;
reqeustStatusUpdateViewModel.CountryCode = countryCode;
if (!ModelState.IsValid)
{
// return View("NewRequestView", Mapper.Map<RequestStatusViewModel>(newReqeustViewModel));
return null;
}
return View("_RequestStatusView", Mapper.Map<RequestStatusUpdateViewModel>(reqeustStatusUpdateViewModel));
}
RequestStatusViewModel
public class RequestStatusViewModel
{
public string RequestStatusCode { get; set; }
public string Status { get; set; }
public int DisplaySequenceNo { get; set; }
}
Script in the main view
function GetCountryCodeFilter() {
alert("Hello");
alert($('#CountryCode').val());
return { countryCode: $('#CountryCode').val() }
};
I'm assuming this is a popup from a kendo grid. The problem is that view gets serialized and sent to the popup when any row is clicked. It doesn't bind the data like you expect in MVC - it sends the same serialized data every time. See here.
So change your hidden to use kendo's MVVM binding so each instance gets the value from the grid row. (CountryCode needs to exist in the grid's datasource):
#Html.HiddenFor(x => x.CountryCode, new { data_bind = "value: CountryCode" }) // underscore becomes dash
Or you could just use:
<input type="hidden" name="CountryCode" id="CountryCode" data-bind = "value: CountryCode"/>

Asp.net mvc 3 model doesn't get automatically bind?

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++
Based on your HttpPost action signature, no, this isn't possible. You're performing an Edit so it will need the Id of the row that is being updated. Since you're not mapping the Id in the action method signature, and the hidden fields holding that information aren't there, the id will never get mapped to the Department model. The result being that you would try to perform an update without a row id.
EDIT: You could modify your action signature like so: Edit(int id, Department department), but then you'd have to manually set the department.Id to the id passed and that would seem to make the model somewhat disjointed.

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.

jQuery UI Datepicker not Posting to Server after Deployment

So I published a ASP.Net MVC3 project that uses the jQuery UI datepickers on to a IIS server. The datepickers don't seem to post their values and revert to default values on the back end.
Locally, though it works like a charm. It's simple jQuery without any options on the datepicker.
Any clue as to why that would happen?
Let me know what I can post to help find a solution.
Thanks!
The model I am trying to post back:
public class Report
{
[Required]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime From { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime To { get; set; }
public virtual Car Car { get; set; }
public virtual IEnumerable<Review> Reviews { get; set; }
}
The form I am using:
#model Cars.Models.Report
<h3>Create a report</h3>
#using (Html.BeginForm("Generate", "Report"))
{
<div>
<div class="span-5">
<div class="editor-label">
#Html.LabelFor(model => model.From)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.From, new { #class = "datepicker lilbig" })
</div>
</div>
<div class="span-5 last">
<div class="editor-label">
#Html.LabelFor(model => model.To)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.To, new { #class = "datepicker lilbig" })
</div>
</div>
<div class="span-11 last">
<div class="prepend-5 last">
<input class="bigbtn" type="submit" value="Create" />
</div>
<div>
#Html.ValidationSummary(true)
#Html.ValidationMessageFor(model => model.From)
#Html.ValidationMessageFor(model => model.To)
</div>
</div>
</div>
}
Method I am posting to:
[HttpPost]
public ActionResult Generate(Report model)
{
try
{
MembershipUser currentUser = Membership.GetUser(HttpContext.User.Identity.Name);
Guid id = (Guid)currentUser.ProviderUserKey;
Car currentCar = CarRepository.Get(id);
currentCar.LastReportCreated = DateTime.Now;
currentCar = CarRepository.Update(currentCar, true);
model.Car = currentCar;
model.Reviews = model.Car.Reviews.Where(s => s.LoggedAt.Date >= model.From.Date &&
s.LoggedAt.Date <= model.To.Date);
return View("Report", model);
}
catch(Exception ex)
{
return View("Error");
}
}
The jQuery looks like this:
$(document).ready(function () {
$(".datepicker").datepicker();
});
Check that the datepicker version on the server is current and running bug-free. Also double check your form data before it gets posted - make sure the date fields have values:
$(document).ready(function () {
$("form").submit(function(){
$(".datepicker").each(function(){
alert( $(this).attr("name") +": "+ $(this).value() );
});
});
});

jQuery and ASP.NEt MVC validation: Prevent validation on server

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);
}
}
} // }

Resources