I have an ASP.NET MVC application with AJAX updating. CRUD operations for every entity are working correctly, except for creating a "Receipt". The problem arises when the controller hits ModelState.IsValid
which translates to false when in fact it should be true. I debugged everything step by step so many times, and it is always false when it should be true.
I am using Entity Framework for entity manipulation and this is the code that was generated from the SQL Server database:
public partial class Receipt
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Receipt()
{
this.Receipts1 = new HashSet<Receipt>();
this.Seminars = new HashSet<Seminar>();
}
public int Id { get; set; }
public System.DateTime IssueDate { get; set; }
public Nullable<System.DateTime> DeliveryDate { get; set; }
public Nullable<System.DateTime> PaymentDue { get; set; }
public Nullable<short> CompanyId { get; set; }
public Nullable<int> Number { get; set; }
public Nullable<int> ClosedReceiptId { get; set; }
public Nullable<decimal> ReturnedAmount { get; set; }
public Nullable<short> ReturnTypeId { get; set; }
public virtual Company Company { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Receipt> Receipts1 { get; set; }
public virtual Receipt Receipt1 { get; set; }
public virtual ReturnType ReturnType { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Seminar> Seminars { get; set; }
}
On top of which I have ReceiptMetadata
for annotations and [Required]
fields:
[MetadataType(typeof(ReceiptMetadata))]
public partial class Receipt
{
}
public class ReceiptMetadata
{
[Required]
public int Number { get; set; }
[Required]
[DisplayName("Issue Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:G}")]
public DateTime IssueDate { get; set; }
[Required]
[DisplayName("Payment Due")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime? PaymentDue { get; set; }
[DisplayName("Return amount")]
[DisplayFormat(DataFormatString = "{0:c}")]
public Nullable<decimal> ReturnedAmount { get; set; }
}
And my Create
function in controller is as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Create([Bind(Include = "Id,IssueDate,DeliveryDate,PaymentDue,CompanyId,Number")]Receipt receipt, int? seminarId)
{
if (seminarId == null)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = "ID is required" });
}
Seminar seminar = db.Seminars.Find(seminarId);
if (seminar == null)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = "Seminar doesn't exist" });
}
int receiptNumber = db.Receipts.Where(r => r.CompanyId == receipt.CompanyId && r.ClosedReceiptId == null && r.IssueDate.Year == DateTime.Now.Year).Count() + 1;
receipt.Number = receiptNumber;
receipt.IssueDate = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.Now, TimeZoneInfo.Local.Id, "Central European Standard Time");
if (ModelState.IsValid)
{
try
{
db.Receipts.Add(receipt);
db.SaveChanges();
seminar.ReceiptId = receipt.Id;
db.Entry(seminar).State = EntityState.Modified;
db.SaveChanges();
GeneratePDF(receipt.Id, receipt.ReceiptNumber, receipt.CompanyId.ToString());
return Json(new { receipt.Id, receipt.PDFLink, Action = "Create", Message = "Receipt successfully added! -> " }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = ex.Message });
}
}
Response.StatusCode = (int)HttpStatusCode.BadRequest;
IEnumerable<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors);
List<string> errorMessages = new List<string>();
foreach (ModelError error in allErrors)
{
errorMessages.Add(error.ErrorMessage);
}
return Json(new { Message = errorMessages });
}
The error I get is
The Number field is required.
when clearly the field Number
is filled in with:
receipt.Number = receiptNumber;
a few lines before the IsValid
.
Can someone please help?
EDIT: this is a screenshot from debugging, the modelstate keys are completely wrong:
The ModelState is already evaluated (and invalid) when entering your action, setting the "Number" property won't evaluate the model again. After setting the property, you may have to manually remove it from the model errors by writing this:
ModelState.Remove("Number");