MVC3 Compare attribute and nested object properties - c#

I have the following:
public class Address
{
public string Email { get; set; }
}
public class CheckoutViewModel
{
public Address Address { get; set; }
[Compare("Address.Email", ErrorMessage = "The email addresses you entered do not match")]
public string ConfirmEmailAddress { get; set; }
}
With client-side JS, this works a treat and validates properly. However, when testing without Javascript enabled, The form posts back but the ModelState error reads:
Could not find a property named Address.Email.
Any ideas as to why this works on the client but not the server? What is the solution in this case?
Many thanks.

If you view the HTML source generated you should find that the input element for Email is called "Address.Email", and this is why the validation works on the client side.
However it looks like the attribute is not built to handle nested properties and so at the server level it is not working (as there is no property called "Address.Email"). As a result you will need to make sure both properties are at the same level (either both on the ViewModel or both on the Address class).
Your best option if probably to put the Email address property onto the view model and then populate the Address object later.

Related

C# RequiredAttribute messages on RESTful service appear double

I'm having a RESTful service done with C# ASP.NET. On my models, I'm using the DataAnnotations' RequiredAttribute (from System.ComponentModel.DataAnnotation). When I'm sending an input model with missing properties (via Swagger or Postman), I get double $"{property} is required" messages, although I only have one [Required] attribute on each property.
I thought maybe it comes from inheritance in my models (I sometimes also have three-level hierarchies and no triple messages, so I think this is not the problem) or from registering the validations double somehow (but where are they registered?). For example, this is one parent:
public class CertificatePayload : AchievementBase
{
public string ExternalLink { get; set; }
}
and this one child class:
public class AchievementBase
{
[Required]
public string GrantedTo { get; set; }
[Required]
public string GrantedBy { get; set; }
}
Any ideas about what I could check or what could be the problem?
Since there came no answer...
A colleague of mine solved the problem with a trick: deleting the required attributes and adding the IValidatable interface to the object with a validate method showed that the validate method was called twice. Stacktrace was the same both times. The right search words found the solution in the end:
StackOverflow-ASP.NET WEB API 2 - ModelBinding Firing twice per request
I had to rebind the ModelValidatorProvider. I applied it to where I had bound my Entities.

ASP.NET MVC - Better UX approach to allowing child object editing than For loop and Partial Views

I have a Contact object that has a number properties, including a child that is a list of Addresses.
public class Contact
{
public int? Id { get; set; }
public string Name { get; set; }
public IReadOnlyList<IAddress> Addresses
{
[Lazy Load Code to populate and return list]
}
[...]
}
I want to allow the user to edit the addresses without having to edit (or post) the whole Contact object. Currently in the UI I have the addresses listed out with an Edit button next each one:
I'm using the Modal syntax that is part of Bootstrap3, and have hidden DIVs that contain a form and the fields to edit an Address. When the user clicks on edit, a modal window form appears that allows the user to edit the Address. Model Binding and validation work within this form.
It works pretty well but I have a few underlying issues with the implementation. I wanted to use the builtin Validation and Model Binding with the Address objects, but I didn't want to post back the whole object just to edit one address.
So I ended up having to create a for loop that writes out the hidden DIVs calling a Partial View and passing Address as the model:
#for (int i = 0; i < Model.Addresses.Count; i++)
{
address = (Address)Model.Addresses[i];
#Html.Partial("_AddressModal", address);
}
The unfortunate side-effect is that the model binding cannot uniquely identify which Address to apply the ModelState to, so Model Binding applies it to all the Address in in the hidden DIVs, instead of the one that was updated. This is because they have the exact same property names.
I've got a work-around that was part of an earlier question. Basically it doesn't write the ModelState unless the object is invalid. For the invalid scenario I don't show the address list which basically hides the problem. Otherwise every edit button would show the same form content.
What I want to know is if there is a better UX approach to allow a user to edit one of the child Addresses in the list?
My goals are to find a better approach using ASP.NET MVC that:
Follow the PRG (Post-Redirect-Get) pattern
Don't redirect to a separate Page/View for editing, which forces the user to navigate back to the contact edit form again
Avoid popups because of the blockers
The solution allows the use Model Binding and Validation for the Address object
What you want is :
1 Form for the contact
Another form for each address
in your controller you will have an action that expects a contact as a parameter (without addresses) and another action that expects an address.
boilerplate code:
[HttpPost]
public RedirectToRouteResult EditAddress(int id, ContactAddressBindingModel address) {
// ...
}
[HttpPost]
public RedirectToRouteResult EditContact(int id, ContactBindingModel contact)
{
// ...
}
public class ContactViewModel : ContactBindingModel
{
public IReadOnlyList<IAddress> Addresses { // ...}
}
public class ContactBindingModel
{
public int? Id { get; set; }
public string Name { get; set; }
}
public class ContactAddressBindingModel : IAddress
{
public int? Id { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
and in the view :
<form action="EditContact">
<!-- contacts inputs etc -->
</form>
// in razor you can do EditorFor(m => m.Addresses) instead of foreach
// and have partialview, or whatever you like.
#foreach (Model.Addresses) {
<form action="EditAddress">
<!-- address inputs etc -->
</form>
}

Conditional Server Side Validation MVC 5

This is, initially at least, more of an exploratory question to see what is and isn't possible and to see if what I would like to achieve is even possible.
At the moment I am using jQuery Unobtrusive validation in conjunction with MVCs standard server side validation to carry out validation before the data is submitted via a web form. Now, I want to have the form be a little more 'dynamic' so depending on the choice of a dropdown/radiobutton/checkbox etc. parts of the form will show or hide themselves, but this is present 2 issues to me:
jQuery Unobtrusive will still attempt to validate all form fields even if they are hidden or have their display set to none. I think I can get around this using some jQuery, a custom class tag on each element and then when the UI is updated, loop through the elements and remove/add the data-val. This isn't the major issue
This is the big one. Since I will have decorated the model with Data Annotations, when the data hits the server, if parts of it are empty, then obviously the if(ModelState.IsValid()) check is going to fail, but I need the annotations there to trigger the client side validation
I'm wondering, is there a way I can override the existing MVC server side validation logic so that if a certain checkbox or dropdown value has been selected, then some logic will simply be bypassed. I've written a very basic custom validation attribute before when I was wanting to limit access based on the connecting parties IP address.
I know I could make use of multiple ViewModels, but when the user first visits the site, the form won't know what type of submission they will be making until they choose the appropriate option, by that point a generic submission object will have already been passed to the View on the HTTP GET controller method and ideally, I'd like to avoid any reloading of the page once a submission has been made to simply load a secondary ViewModel. I'm also making use of a file uploader and file information and so on is all stored within the ViewModel so this would also need to be passed around which I could see getting messy.
public class Submission
{
[HiddenInput(DisplayValue = false)]
public Guid SubmissionID { get; set; }
[Required(ErrorMessage = "Please provide your name")]
public string Name { get; set; }
[EmailAddress(ErrorMessage = "Invalid email address")]
[Required(ErrorMessage = "Please enter an email address")]
public string Email { get; set; }
[Required(ErrorMessage = "Please provide a contact number")]
[StringLength(13, ErrorMessage = "Phone number can be no more than 13 digits long")]
public string Phone { get; set; }
public string SubmissionPurpose { get; set; }
//Suspicious Transaction
//This should only be validated if, on the POST, the SubmissionPurpose matches the appropriate value
[Required(ErrorMessage = "Please provide a date")]
public string TransDateNoticed { get; set; }
public string TransAdditionalDetails { get; set; }
//Support Query
//This should only be validated if, on the POST, the SubmissionPurpose matches the appropriate value
[Required(ErrorMessage = "Please provide a date")]
public string SupDateNoticed { get; set; }
public string SupAdditionalDetails { get; set; }
//Additional Data
public string Message { get; set; }
public List<UploadedFile> Attachments { get; set; }
public IEnumerable<SelectListItem> SubmissionPurposes()
{
return new List<SelectListItem>
{
new SelectListItem() {Value = "", Text = "Please select an option"},
new SelectListItem() {Value = "Suspicious Transaction Reporting", Text = "Suspicious Transaction Reporting"},
new SelectListItem() {Value = "Request a Support Session", Text = "Request a Support Session"}
};
}
I'm pretty open to any and all suggestions, I'm thinking some sort of custom data annotation is the best way to go since that seems to be the only way I could have granular control over it, but then my question would be how to have an annotation like the idea I'm currently trying to work towards below
[CustomRequirement, SumissionPurpose = "Suspicious Transaction Reporting"]
I know in MVC 5.1 they introduced a new DropdownForEnum feature but from my looking so far I can't see anything new in 5.1 or 5.2 that might allow me to meet this need either.
I've used Foolproof Validation in the past. They provide several extra attributes and the clientside validation.
[Is]
[EqualTo]
[NotEqualTo]
[GreaterThan]
[LessThan]
[GreaterThanOrEqualTo]
[LessThanOrEqualTo]
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
Custom validation attributes are the way to go. Please note that if the properties in the model are complex objects and their validations depend on the parent object properties you will not get the validation context of the parent object property. In that case you have to think of writing your own model validators implementing from ModelValidator class and adding it to ModelValidatorProvider factory class.

How do I prevent Entity Framework forcing validation on child object properties?

I have an ASP.NET MVC 3 project that uses .Net 4.0 Framework and Entity Framework 4.4. I am using the code first w/ migrations approach for the Entity Framework.
I have an object A that has a property of object B. object A really just needs to know the id for object B, but MVC is enforcing all annotation validations on object B as I've marked it required in object A (which it is). ModelState.IsValid is always returning false because some validations are failing on object B when the form is submitted.
Example:
public class FormField
{
public int Id { get; set; }
[MaxLength(50)]
[Required]
[DisplayName("Field Name")]
public string Name { get; set; }
[Required]
public Form Form { get; set; }
}
public class Form
{
public Form()
{
Fields = new List<FormField>();
}
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<FormField> Fields { get; set; }
}
This isn't a problem when editing an existing FormField as I can just put a hidden field with the Form.Name property on the page (this still strikes me as something that should be unnecessary). The issue arises when creating a new FormField. I display a drop down list of forms (this is populated from my view model), and make that field point to the FormField.Form.Id property. ASP .NET MVC is still expecting formField.Form.Name (as this was marked as required on the Form object).
If I remove the "[Required]" annotation from the Form field of the FormField object, the validations wouldn't fire, but this would make the foreign key to Form.Id nullable in the database, which it shouldn't be.
Any thoughts? I'm probably doing something wrong here but I'm not entirely sure what.
I removed the [Required] annotation on FormField.Form. That didn't fix the issue. I tried adding "[Bind(Exclude = "vm.FormField.Form")]" to the parameter being posted. This stops the binding, but ensures I'm still left with the validation errors in ModelState.
Ultimately, I had to do:
ModelState.Remove("FormField.Form.Id");
ModelState.Remove("FormField.Form.Name");
And this prevented the ModelState errors I was getting. As I really only need the Form.Id, I had a property in my ViewModel "SelectedForm" and use this property for that value.
So...this works, but...this seems a pretty tedious solution as if I had more required fields on the FormField.Form object, I would have to list each one as well as hard code the property name in a string form.
Can anyone think of a way to refine this approach?
Maybe you could remove the [Required] attribute and instead use the EF fluent API to configure this:
modelBuilder.Entity<FormField>().Property(ff => ff.Form).IsRequired();

Conditional JSON Deserialization using C#

I'm writing a C# test automation to validate web services that return JSON strings. I created a DataContract that mapped to what was being returned. Assume this is what was being returned:
{"DataModule" : {"required":"false", "location":"toolbar"}}
My test automation was working fine, but then I started getting this error:
"The data contract type 'DataModule' cannot be deserialized because
the required data members 'required, location' were not found."
I checked the JSON and the data module was now
{"DataModule" : {"width":"400", "height":"320"}}
I discovered that the dev implementation is that if the first type of data module is encountered, the client parses that and creates a button on a toolbar. If the second type of data module is returned, the button appears on the toolbar AND a panel appears in another location with those measurements.
Is there a way to either create optional members in a data contract OR implement conditional deserialization to account for JSON objects that may have multiple implementations?
If you declare the model with all of the likely properties, only the ones found in the JSON string will be populated:
public class DataModule
{
public bool Required { get; set; }
public string Location { get; set; }
public string Width { get; set; }
public string Height { get; set; }
}
#dave-s
I had already tried adding all of the properties, but since I knew I was on the right track, your suggestion keyed me into something else. The properties were all decorated with
[System.Runtime.Serialization.DataMemberAttribute(IsRequired = false)]
But the class itself, was decorated with only [Serializable]. When I changed [Serializable] to
[System.Runtime.Serialization.DataContractAttribute()]
it started working.

Categories

Resources