ASP.NET CORE DataFormat is effecting Edit action - c#

In my project I have properties that I would like to be displayed in dollar format.In my class I have it set up to do so
public class Worker{
public int Id {get;set;}
public string Name {get;set;}
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public double Rev {get;set;}
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public double Cost {get;set;}
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public double Profit {get;set;}
}
This works and displays properly however when I go to the edit page All of the info is displayed in this format and I have to change every field from the dollar sign to the double or I get an error. Here is a screenshot showing.
Even if I want to change one field I have to rewrite every field from $50,000 to 50000.00. How can I fix it so in Edit I don't have to worry about fixing every field, and my dollar sign format will be displayed in Index or Details and so on? Here is my controller for Edit
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var worker = await _context.Workers.FindAsync(id);
if (worker == null)
{
return NotFound();
}
return View(worker);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Rev,Cost,Profit")] Worker worker)
{
if (id != worker.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(worker);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!WorkerExists(worker.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(worker);
}
At this point I am not concerned whether the currency format is displayed in edit view or just a number. I just do not want to have to convert every field from currency to a number in order to save the updates on one single property. How can I get rid of this error? Either leaving in this format or in number format.

Even if I want to change one field I have to rewrite every field from $50,000 to 50000.00. How can I fix it so in Edit I don't have to worry about fixing every field, and my dollar sign format will be displayed in Index or Details and so on?
IMO, you just need to set ApplyFormatInEditMode=false or remove it from model.
public class Worker
{
public int Id { get; set; }
public string Name { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = false)]
public double Rev { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = false)]
public double Cost { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = false)]
public double Profit { get; set; }
}

Related

Setting condition for action method

I have a table used to track a patient appointments in a clinic.
Check In and out are used to track his presence.
I need to be able to create an appointment only if he had checked out
public class Appointment
{
[Key]
public virtual int ID { get; set; }
[Required]
[Display(Name = "Case Number")]
public virtual string MRN { get; set; }
[Required]
[Display(Name = "Department")]
public virtual int DepID { get; set; }
[Required]
[Display(Name = "Check IN")]
public virtual DateTime Check_IN { get; set; }
[Display(Name = "Check Out")]
public virtual DateTime? Check_OUT { get; set; }
}
I created this method inside the controller to check whether or not he checked out.
private bool CheckedOut (string MRN)
{
return _context.Appointments.Any(a => a.MRN == MRN && !a.Check_Out.HasValue);
}
And my Create action method.
public async Task<IActionResult> Create(string MRN, Appointment appointment)
{
ViewData["MRN"] = MRN;
if (CheckedOut(appointment.MRN))
{
_context.Add(appointment);
await _context.SaveChangesAsync();
return RedirectToAction("Index", "Patients");
}
return View();
}
But the appointment is still created even if the patient has one which he hadn't checked out from.
Any ideas?
Does it create an appointment if the User has checked out?
Because to me it looks like you are checking if any open appointments exist and returning true on that.
should be:
return !_context.Appointments.Any(a => a.MRN == MRN && !a.Check_Out.HasValue);
the problem has to be you LINQ query.
i would suggest that you extract variables and set a breakpoint to see what is going on:
private bool CheckedOut (string MRN)
{
var mrn = MRN;
var hasCheckedOut = a.Check_Out.HasValue;
return _context.Appointments.Any(a => a.MRN == mrn && !hasCheckedOut);
}

Validating form when model is a collection of sub models

I've got a view which needs several models to work correctly. So, I created a model which is a collection of multiple (sub) models. This is the model.
public class PolicyDetail
{
public Policy Policy { get; set; }
public IEnumerable<Insured> Insureds { get; set; }
public IEnumerable<Risk> Risks { get; set; }
public IEnumerable<Construction> Constructions { get; set; }
}
And here's an example of what one of the sub models look like, which is an actual entity from the database:
public class Policy
{
[Key]
public int PolicyID { get; set; }
[DisplayName("Policy Number")]
public Guid PolicyNumber { get; set; }
[Required(ErrorMessage = "Please enter a valid Effective Date.")]
[DataType(DataType.DateTime)]
[DisplayName("Effective Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime EffDate { get; set; }
[Required(ErrorMessage = "Please enter a valid Expiration Date.")]
[DataType(DataType.DateTime)]
[DisplayName("Expiration Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ExpDate { get; set; }
public Boolean IsActive { get; set; }
}
This was all working well, right up until I tried to submit a form with errors in it to test the validation. I should have seen this coming (maybe?) but because the actual model doesn't have any validation tags on it, it always passes the if (ModelState.IsValid) check. Is there some way to enforce, or inherit, all of the Data Annotations from the sub classes?
Or, am I going about this all wrong, using a model which is a collection of other models? The thing is, I want to be able to edit/add multiple db entities from the same view.
EDIT:
This article by Josh Carroll looks to be EXACTLY what I need. But when I implement it, I get a Null Object error. Here's what I'm doing:
public class PolicyDetail
{
[Required, ValidateObject]
public Policy Policy { get; set; }
public IEnumerable<Insured> Insureds { get; set; }
public IEnumerable<Risk> Risks { get; set; }
public IEnumerable<Construction> Constructions { get; set; }
}
Then in the override method he provides:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var results = new List<ValidationResult>();
var context = new ValidationContext(value, null, null);
Validator.TryValidateObject(value, context, results, true);
if (results.Count != 0)
{
var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName));
results.ForEach(compositeResults.AddResult);
return compositeResults;
}
return ValidationResult.Success;
}
}
the parameter "value" comes in null, so it errors on this line:
Validator.TryValidateObject(value, context, results, true);
Am I missing something? Doing something wrong?
You can manually call the validations on the sub-models using this: https://msdn.microsoft.com/en-us/library/dd411772.aspx
var context = new ValidationContext(model.Policy, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model.Policy, context, validationResults, true);
You can then use the ModelState.AddModelError to build the response from that.
Definitely not the most elegant possible solution, but might be easier than rewriting what you have.

MVC 4 Conditional Model Validation with Entity Framework

Is it possible to place conditions on a ViewModel where data equals a specific value in one field within the entity framework, the required element or entire object is removed from the ViewModel before using TryValidateModel on the ViewModel?
I would like to remove HomeValue and PurchasePrice from the Model validation where OwnOrRent (model.OwnOrRent) is not equal to 1 or 9.
Model.cs
public class Address
{
public int Id { get; set; }
[DisplayName("House Name or Number")]
[StringLength(50)]
public string HouseNameOrNumber { get; set; }
[DisplayName("Post Town")]
[StringLength(50)]
public string PostTown { get; set; }
[Required(ErrorMessage = "Own or Rent is Required")]
[DisplayName("Own or Rent")]
[StringLength(50)]
public string OwnOrRent { get; set; }
[Required(ErrorMessage = "Mortgage/Rent Amount is Required")]
[DisplayName("Mortgage/Rent Amount")]
[StringLength(50)]
public string MortgageRent { get; set; }
[Required(ErrorMessage = "Home Value is Required")]
[DisplayName("Home Value")]
[StringLength(50)]
public string HomeValue { get; set; }
[Required(ErrorMessage = "Purchase Price is Required")]
[DisplayName("Purchase Price")]
[StringLength(50)]
public string PurchasePrice { get; set; }
}
HomeController.cs
public ActionResult Application(int id)
{
var viewAddressModel = new Address();
using (
var dbEntities = new DbEntities(new EntityConnection(_df.DbEntities)))
{
var model = dbEntities.Applications.Single(e => e.Id == id);
viewAddressModel.Id = Id;
viewAddressModel.HouseNameOrNumber = model.HouseNameOrNumber;
viewAddressModel.PostTown = model.PostTown;
viewAddressModel.OwnOrRent = GetStatus(model.OwnOrRent);
viewAddressModel.MortgageRent = model.MortgageRent.ToString();
viewAddressModel.HomeValue = model.HomeValue;
viewAddressModel.PurchasePrice = model.PurchasePrice;
if (model.OwnOrRent != "1" || model.OwnOrRent != "9")
{
ModelState.Remove("HomeValue");
ModelState.Remove("PurchasePrice");
}
if (!TryValidateModel(viewAddressModel))
{
return PartialView("Address", viewAddressModel);
}
}
var vm = new ApplicationViewModel { Item = CreateApp(id) };
return PartialView("Application", vm);
}
As you can see I have tried to use ModelState.Remove but this has no effect.
Any assistance with this would be much appreciated?
Based on your comments you want to populate a model from the database, then validate it (because its old data which may not be valid), but not display errors for HomeValue or PurchasePrice based on the value of OwnOrRent, in which case you need to call TryValidateModel first, then remove ModelState errors
var viewAddressModel = new Address();
.... // set values
if (!TryValidateModel(viewAddressModel))
{
if (model.OwnOrRent != "1" || model.OwnOrRent != "9")
{
if (ModelState.ContainsKey("HomeValue"))
{
ModelState["HomeValue"].Errors.Clear();
}
if (ModelState.ContainsKey("PurchasePrice"))
{
ModelState["PurchasePrice"].Errors.Clear();
}
}
}
You can now use if (ModelState.IsValid) to check if there are any other validation errors and return the appropriate view
Side note: I just used your if condition relating to the OwnOrRent value, but I suspect what you really want is
if (!(model.OwnOrRent == "1" || model.OwnOrRent == "9"))
There is a thread about the different options to do conditional validation:
ASP.NET MVC Conditional validation

Fluent Validation Inconsistent with ASP.NET MVC 5

I'm using Fluent Validation v5.5 with ASP.NET v5.2.2 and I'm getting some inconsistent results with the validation.
My view model is:
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
My controller method is:
public ActionResult Quote(QuoteViewModel viewModel)
{
var _validator = new QuoteValidator();
var results = _validator.Validate(viewModel);
if (!ModelState.IsValid)
{
return Json(false);
}
return Json(true);
}
My validator is:
public class QuoteValidator : AbstractValidator<QuoteViewModel>
{
public QuoteValidator()
{
RuleFor(x => x.Gender).NotEmpty();
RuleFor(x => x.StateId).NotEmpty();
RuleFor(x => x.DateOfBirthDay).NotEmpty().InclusiveBetween(1, 31);
RuleFor(x => x.DateOfBirthMonth).NotEmpty().InclusiveBetween(1, 12);
RuleFor(x => x.DateOfBirthYear).NotEmpty().LessThanOrEqualTo(DateTime.UtcNow.Year);
}
}
I'm running a test that posts all blank value form fields. Thus the view model fields retain default values after the view model object is created.
For comparison, in the controller I'm running the validation explicitly and the results aren't consistent with the validation result in ModelState.
ModelState is showing 4 errors, all triggered by NotEmpty rules. NotEmpty on the nullable enum Gender doesn't seem to trigger.
The explicit validation is returning 7 out of 8 errors, the LessThanOrEqualTo rule won't fire since the DateOfBirthYear defaults to zero.
My pain point is I can't figure out why ModelState is missing the NotEmpty error on the nullable enum Gender.
The only way I've been able to trigger that error is to post just the Gender value.
Please help.
EDIT:
After stepping through some code, it appears that the issue is related to the Fluent Validation RequiredFluentValidationPropertyValidator. The Gender field is a nullable value type which is bound to null. The following snippet from RequiredFluentValidationPropertyValidator prevents validation:
ShouldValidate = isNonNullableValueType && nullWasSpecified;
!ModelState.IsValid doesn't use your validation result it uses defaulf MVC validation (that can be added through DataAnnotations). You have to check !results.IsValid instead which contains the validation result of your QuoteValidator.
If you want to use default ModelState.IsValid you have to mark your model with validator attribute:
[Validator(typeof(QuoteValidator))]
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
And add the following line to your Application_Start method:
protected void Application_Start() {
FluentValidationModelValidatorProvider.Configure();
}

Validation using Regular Expression

Im having some problems utilizing RegularExpression attribute in a ASP.net MVC project.
It seems to work client side, it goes away when it fits, however then upon post action, the model state is checked for being valid, it ends up posting error, that it must follow the regular expression.
I have tried theese following:
^[0-9]{1,2}/[0-9]{1,2}/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}$
^\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}$
Essentially it must catch 14/12/2014 14:20 as input.
Any ideas? I'm lost.
Model:
[Required]
[Display(Name = "TimeDisplay", ResourceType = typeof(Resources.Models.Log))]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy HH:mm}")]
[RegularExpression(#"^[0-9]{1,2}/[0-9]{1,2}/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}$")]
public DateTime Time { get; set; }
Controller Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Log log)
{
if (ModelState.IsValid)
{
db.Logs.Add(log);
db.SaveChanges();
TempData["Notification"] = AlertGenerator.GenerateAlert(AlertGenerator.Level.Success, "Success! Log Saved");
return RedirectToAction("Index");
}
return View(log);
}
As I know, MVC will use current CultureInfo (on server) to parse DateTime format, so you cannot directly binding "dd/MM/yyyy HH:mm" to your entity.
My solution is creating ViewModel then use DateTime.ParseExact(...) to parse the date:
Model:
[Display(Name = "TimeDisplay", ResourceType = typeof(Resources.Models.Log))]
[Required]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy HH:mm}")]
public DateTime Time { get; set; }
ViewModel
[Display(Name = "TimeDisplay", ResourceType = typeof(Resources.Models.Log))]
[Required]
public string Time { get; set; }
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(LogViewModel logViewModel)
{
if (ModelState.IsValid)
{
// convert from ViewModel to Entity Model
Log log = new Log(logViewModel);
// parse the time
log.Time = DateTime.ParseExact(logViewModel.Time, "dd/MM/yyyy HH:mm", null);
db.Logs.Add(log);
db.SaveChanges();
TempData["Notification"] = AlertGenerator.GenerateAlert(AlertGenerator.Level.Success, "Success! Log Saved");
return RedirectToAction("Index");
}
return View(log);
}

Categories

Resources