ASP.NET MVC 5 prevent value changes - asp.net

When I update my website, it hints me this problem "{"The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.\r\nThe statement has been terminated."}"
The screenshot is list below, there is a value named RecordDate, it has value, but I will not change anything about that value so I didn't display it on the screen.
The problem is MVC automatically update that value for me, and the value of the date becomes 0000-00-01 i think, maybe something else, how to prevent it? just keep the origin value and update other columns.
The model class looks like this
public class ShiftRecord
{
public int ID { get; set; }
public int EmployeeID { get; set; }
[Display(Name="Company Vehicle?")]
[UIHint("YesNo")]
public bool IsCompanyVehicle { get; set; }
[Display(Name="Own Vehicle?")]
[UIHint("YesNo")]
public bool IsOwnVehicle { get; set; }
//Problem comes from this line
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString="{0:yyyy-MM-dd}")]
public DateTime RedordDate { get; set; }
[Display(Name="Day Type")]
public Nullable<DayType> DayType { get; set; }
[Display(Name="Normal Hrs")]
public Nullable<int> NormalHours { get; set; }
[Display(Name="Time and Half Hrs")]
public Nullable<int> TimeAndHalfHours { get; set; }
[Display(Name="Double Time Hrs")]
public Nullable<int> DoubleTimeHours { get; set; }
[Display(Name="Shift Hrs")]
public Nullable<int> ShiftHours { get; set; }
public string Comment { get; set; } // System manager can leave any comment here
public bool IsRead { get; set; } // has this shift record been read
public virtual Employee Employee { get; set; }
public virtual ICollection<JobRecord> JobRecords { get; set; }
}
In the controller, I didn't change anything about the model, so it looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,EmployeeID,IsCompanyVehicle,IsOwnVehicle,RecordDate,DayType,NormalHours,TimeAndHalfHours,DoubleTimeHours,ShiftHours,Comment,IsRead")] ShiftRecord shiftrecord)
{
if (ModelState.IsValid)
{
db.Entry(shiftrecord).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.EmployeeID = new SelectList(db.Employees, "ID", "LastName", shiftrecord.EmployeeID);
return View(shiftrecord);
}
And I didn't change Edit view as well, the only thing is I made RecordDate unchangeable, changed it from #Html.EditorFor to #Html.DisplayFor
<div class="form-group">
#Html.LabelFor(model => model.RedordDate, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => model.RedordDate)
#Html.ValidationMessageFor(model => model.RedordDate)
</div>
</div>

Your issue is .net uses a default 1/1/0001 datetime min value, and the sql minimum value is 1/1/1753, which is incompatible. If you use datetime?, it will resolve as null and work OK, or put in code to update the date to a default value before committing to the database.

Your understanding is incorrect. ASP.NET MVC did not automatically update the value for you, the problem arises because you did not post RedordDate to the controller action so RedordDate will have its default value (i.e. default(DateTime)).
DateTime is a value type in .NET such that it cannot be null and its default value is DateTime.MinValue (i.e. 01/01/0001 00:00:00).
You can solve it by making the RedordDate property Nullable by changing its type from DateTime to DateTime? so that it accepts null values.
One thing to note is that if you save this value back to a SQL Server but your underlying SQL datatype is datetime instead of datetime2, you will receive an exception since 01/01/0001 00:00:00 is out-of-range in datetime
Further reading:
MSDN recommends using datetime2 in a new development
Difference between value types and reference types explained by Jon Skeet

You do render any controls for property RedordDate so when you post back, the DefaultModelBinder initializes a new instance of ShiftRecord and RedordDate has a value of DateTime.MinValue (1/1/0001).
Add a hidden control for the property to post it back
#Html.HiddenFor(m => m.RedordDate)
Then in the POST method, remove the [Bind] attribute. Currently, even if the value is posted back, it will not bind because it has been excluded from the Include list. You have RecordDate but not RedordDate (a typo?). Note by default all properties will be bound so the attribute is not necessary unless you are specifically excluding properties.
A better alternative is to create a view model that contains only those properties you want to display and edit (What is a view model in MVC) and then in the POST method, get the original data model and map the view model properties to it.
Side note: Can the vehicle be both IsCompanyVehicle and IsOwnVehicle?

Related

Issue while passing null values to nullable properties in web api call in .netcore web api project

I am facing issue while passing null parameter values to properties of my model in HttpGet verb.
I am using .Net Core 2.1 for my web API project. Below is my action method in controller:
[HttpGet("get")]
public ActionResult GetData([FromQuery]MyTestModel model)
{
var result = new MyTestModel();
return new JsonResult(result);
}
And my MyTestModel.cs is like :
[Serializable]
public class MyTestModel
{
public MyTestModel()
{
PageNo = 1;
PageSize = 10;
}
public int ClientId { get; set; }
public int? CandidateId { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public int PageNo { get; set; }
public int PageSize { get; set; }
}
When I call the API like :
api/controller/get?clientId=7583&candidateId=null&fromDate=null&toDate=null
I am getting 400 response. Below is the response message:
{"toDate":["The value 'null' is not valid for ToDate."],
"fromDate":["The value 'null' is not valid for FromDate."],
"candidateId":["The value 'null' is not valid for CandidateId."]
}
When I don't send nullable properties at all(candidateId, fromDate,toDate), this hits my action and uses default values as null.
What's the problem if I am trying to explicitly setting null values?
Do I need to set some configuration in my Startup.cs to handle null values for nullable properties?
Any help will be appreciated .
Thanks in advance.
Everything sent in the query string is just a string. So, when you do something like toDate=null, you're actually saying "set toDate to "null"", i.e. the string "null". The modelbinder attempts to convert all the strings to the actual types you're binding to, but there's no conversion available that can turn "null" into a null DateTime.
To set the value to null, you need to either pass no value toDate= or just omit the key entirely from the query string.

Validating that a Reason field is filled if Date field is today

I use ASP.NET MVC4 in my solution. I have the ViewModel below where I would like to validate that the field EmergencyReason is filled only if the field Date is today. I try this:
public class LoadingViewModel
{
public DateTime Date { get; set; }
[RequiredIf("Date", Comparison.IsEqualTo, DateTime.Today)]
public string EmergencyReason { get; set; }
...
}
It doesn't work. The third argument of RequiredIf must be a constant expression, ...
Any idea how can I force the user to enter an EmergencyReason only if Date field is today?
Thanks.
You seem to be using some non-standard RequiredIf attribute which is not part of the standard ASP.NET MVC 4 package.
As you know C# allows you to only pass constant values to attributes. So one possibility is to write a custom attribute:
public class RequiredIfEqualToTodayAttribute: RequiredIfAttribute
{
public RequiredIfEqualToTodayAttribute(string field)
: base(field, Comparison.IsEqualTo, DateTime.Today)
{
}
}
and then:
public class LoadingViewModel
{
public DateTime Date { get; set; }
[RequiredIfEqualToToday("Date")]
public string EmergencyReason { get; set; }
...
}
C# doesn't support DateTime literals, a workaround for this is to use a String like this, but it won't resolve your problem. I suggest you move the validation code inside the Controller and return a ModelState.AddModelError("EmergencyReason", "Emergency Reason is required")

ASP.NET MVC3 - problem handling multiple values from a strongly typed Listbox

I'm having a problem handling multiple values from a strongly typed Listbox.
I have an Event Class that can have multiple Technology classes.
Here's my simplified Event class:
public class Event
{
public int ID { get; set; }
public IEnumerable<Technology> Technologies { get; set; }
}
I was using this
public List<Technology> Technologies { get; set; }
and changed to
public IEnumerable<Technology> Technologies { get; set; }
but still got the same error.
Here's the Technology class, it's a really simple one
public class Technology
{
public int ID { get; set; }
public String Description{ get; set; }
}
here's my simplified Controller
public ActionResult Create()
{
var TechnologyQry = from d in db.Technology
orderby d.Description
select new { d.ID, d.Description };
ViewBag.eventTechnology = new MultiSelectList(TechnologyQry ,"ID","Description");
return View();
}
and here's the view portion that renders the ListBox
#Html.ListBoxFor(model => model.Technologies, (MultiSelectList)ViewBag.eventTechnology)
and thit's the error I'm getting
The ViewData item that has the key 'Technologies' is of type 'System.Collections.Generic.List`1[[stuff.Models.Technology, stuff, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' but must be of type 'IEnumerable'./
Sorry, English is not my main language.
I'd appreciate any help I can get.
Thank you.
What Model have do you have defined in the View #model ...?
Besides that, the Create method isn't returning something to be used for/as the model.
If you want the listbox to be called "Technologies" in the name attribute, you could instead use:
#Html.ListBox("Technologies", (MultiSelectList)ViewBag.eventTechnology)

ASP.NET MVC3 DropDownListFor Enum

Hi I'm fairly new to MVC3 and I'm trying to do something that I think must be fairly common, but can't quite get it.
I have a model I wish store the value for enum DayOfWeek in:
public class Booking
{
public int ID { get; set; }
public int Day { get; set; }
....
}
I've made it an int to store in the database.
I want to edit in the View as a DropDownList:
<div class="editor-field">
#Html.DropDownListFor(model => model.Booking.Day, new SelectList(Enum.GetValues(typeof(DayOfWeek))))
#Html.ValidationMessageFor(model => model.Booking.Day)
</div>
However I get the error: "The field day must be a number".
I know I'm missing something, and probably something simple, can anyone help?
Add a SelectList property to your viewmodel and populate as per #Brandon's answer here.
Then you will change your code to:
#Html.DropDownListFor(model => model.Booking.Day, Model.SelectListProperty)
(where SelectListProperty is the name of your property on your viewmodel)
You can also change your model class using DayOfWeek:
public class Booking
{
public int ID { get; set; }
public DayOfWeek Day { get; set; }
}

Populate dropdownlist from another dropdownlist with objects in MVC 2

I am trying to develop a simple MVC 2 timesheet application for my small business.
I have a sort of mock model for now until I have a database in place, just to make things simpler while I develop the functionality. It consists of the following:
public class CustomersRepository
{
public CustomersRepository()
{
Customers = new List<Customer>();
}
public List<Customer> Customers { get; set; }
}
public class Task
{
public Task()
{
Customer = new Customer();
TimeSegments = new List<TimeSegment>();
}
public override string ToString()
{
return Name;
}
public string Name { get; set; }
public Customer Customer { get; set; }
public List<TimeSegment> TimeSegments { get; set; }
}
public class TimeSegment
{
public string Id { get; set; }
public string Date { get; set; }
public int Hours { get; set; }
}
public class Customer
{
//To show the name in the combobox instead of the object name.
public override string ToString()
{
return Name;
}
public Customer()
{
Tasks = new List<Task>();
}
public List<Task> Tasks { get; set; }
public string Name { get; set; }
}
I initialize the repository in the controller, and pass the "model" to the view:
CustomersRepository model = new CustomersRepository();
public ActionResult Index()
{
InitializeRepository();
return View(model);
}
Now, in the view I populate a dropdownlist with the customers:
<div>
<%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers), new {#id="customerDropDownList"}) %>
</div>
But then I need to populate a second dropdownlist (taskDropDownList for the tasks associated with a particular customer) based on the selection the user chooses in the customer dropdownlist.
But how do I do this exactly? I have seen examples with jQuery, but I'm not sure how to apply them to this situation. Also, the examples seem to just populate the lists with string values. I need to be able to access the objects with all their properties. Because the next thing I need to do is to be able to populate the TimeSegments list of the selected task with values from input fields (i.e. the hours worked for particular dates). And for that to be saved to the "model" (eventually to the database) in the controller, how do I get it there, unless the objects are all part of the same model bound to the View?
I'm on rather thin ice with this since I still find the connection between the View and the Controller hard to handle, compared with e.g. Windows development, where these things are rather easy to do. So I would really appreciate a good step by step example if anyone would be so kind as to provide that!
I found the answer here:
http://www.pieterg.com/post/2010/04/12/Cascading-DropDownList-with-ASPNET-MVC-and-JQuery.aspx
It needed some tweaks, and I got help here from CGK. See this post:
Cascading dropdownlist with mvc and jQuery not working

Resources