I have an ASP.NET application which requires the user to fill in a form to create a new object. It is defined as follows:
public class Address
{
public int ID { get; set; }
[Required]
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
[Required]
public string City { get; set; }
public string Postcode { get; set; }
public string AirportCode { get; set; }
}
The fields AddressLine1 and City are required when filling out the create form. However, a new development has introduced the field AirportCode, which changes the requirements. Basically, if an airport code value is given, no other values are required. If not, the address line 1 and city values are both still required. If the user neglected to fill out any of the form, I would expect all three fields to display an error message until wither the City and AddressLine1 values or the AirportCode value was given, but I can worry asbout the semantics of it later - the important thing is to not let an invalid object get through.
I guess the logic would be:
if AirportCode OR (AddressLine1 AND City)
Here is a small sample of the City input field. It's the default generated by the application:
<div class="form-group">
<label asp-for="City" class="col-md-2 control-label">City</label>
<div class="col-md-10">
<input asp-for="City" class="form-control" />
<span asp-validation-for="City" class="text-danger"></span>
</div>
</div>
Is there a way to go about implementing this without resorting to JavaScript? The form validation has been so neat and easy so far, and I would love to be able to do this through the framework. Thanks.
An afterthought: I may later on want to grey out the other fields if AirportCode started getting filled out. It's not strictly related to validation but it might tie into this?
You would need to write your own custom validation attribute.
Howerver there is a plugin called "ExpressiveAnnotations" which is very easy to use and helps you implement logic into your "DataAnnotations".
[RequiredIf("AirportCode == null",
ErrorMessage = "Your error message.")]
public string AddressLine1 { get; set; }
[RequiredIf("AirportCode == null",
ErrorMessage = "Your error message.")]
public string City { get; set; }
[RequiredIf("City == null")]
[RequiredIf("AddressLine1 == null")]
public string AirportCode { get; set; }
If you feel it would be useful for your purposes, more information about ExpressiveAnnotations library can be found here. Client side validation is also supported out of the box.
A small .NET and JavaScript library which provides annotation-based
conditional validation mechanisms. Given attributes allow to forget
about imperative way of step-by-step verification of validation
conditions in many cases. Since fields validation requirements are
applied as metadata, domain-related code is more condensed.
Related
I have a project utilizing ASP.NET MVC and Razor page layouts. The page in question will be a survey whose questions, datatypes, and answers have been configured by an admin user and retrieved from a database. For example:
public class ExampleViewModel
{
//the user define dquestion
public string Question1Text { get; set; }
//this is an enum with "Text","YesNo","DropDown"
public AnswerType Question1Type { get; set; }
//this would hold options for the drop down list
public string Question1Options { get; set; }
//the user input answer
public string Question1Answer { get; set; }
}
What I am not sure is how to structure the Razor view to create the appropriate type of form input field depending on the AnswerType. I seem to recall something about creating templates for the various DataType() annotations but I am not sure where to start looking at that and if that applies in this case?
You want to use Templated Helpers - Here is a good walkthrough - http://www.hanselman.com/blog/ASPNETMVCDisplayTemplateAndEditorTemplatesForEntityFrameworkDbGeographySpatialTypes.aspx
In the helper itself you can do stuff like:
#if (model.AnswerType is xxx)
{
<button> xxx </button> - or your html
}
etc
I have the following model:
public class Contact
{
public Contact()
{
Name = "Your Name";
Email = "Your Email";
Message = "Your Message";
}
[Required]
[StringLength(60,MinimumLength = 3)]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[RegularExpression(#"\b[A-Z0-9._%-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b")]
public string Email { get; set; }
[Required]
[StringLength(2200, MinimumLength = 10)]
[DataType(DataType.MultilineText)]
public string Message { get; set; }
}
For Message and Name, their default values (in the constructor) actually pass validation, obviously that is bad. I know I could check for this and throw an error in the Controller, but I'm trying to find a way to do these in the model (as I assume that is the correct place to do it).
I wouldn't do this at all server side. Use a textbox watermark ala one of the many methods for ex.
http://code.google.com/p/jquery-watermark/
Those look like hints, not default values. You should implement these with javascript, instead of setting them as input values.
My project is in ASP.NET MVC 3. In my domain I have a model object defined by Entity Framework. In order to validate properties I simply extend the generated EF object and add a metadata class which contains all my validation attributes.
[MetadataType(typeof(ContactInformationMetaData))]
public partial class ContactInformation
{
}
public class ContactInformationMetaData
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string Phone { get; set; }
[EmailValidator]
public string Email { get; set; }
}
This works great most of the time but now I have a more complex scenario. I don't want the Phone and Email properties to be required but I want ONE of them to be required. In other words, I want to require that either email or phone or both be set, but not none.
How would I perform complex validation like this? If I create a custom validation attribute where would I put it and how would that work?
Here is another question exactly like that (even the Phone and Email example is the same):
Model Validation / ASP.NET MVC 3 - Conditional Required Attribute
Check out FluentValidation:
http://fluentvalidation.codeplex.com/wikipage?title=mvc
you can easily create this type of custom validation, it's a pretty cool validation framework in general
using ASP.NET MVC, I have a Model, to which I'm attaching attributes so that I can get use the MVC Model bound validation, but to doesn't this break the rule of MVC, where you are putting items that belong to the View, into the Model? I hope I don't come of as trying to be smart, I am however curious as to other's opinions.
public class Payments
{
[DataType(DataType.Text)]
[DisplayFormat(NullDisplayText="")]
[Display(Name="Payment Id")]
[Required(ErrorMessage="Required")]
public int PaymentId { get; set; } //todo: make this into a dropdown
[DataType(DataType.Text)]
[Display(Name="Bill Name")]
[Required(ErrorMessage = "Required")]
public string PaymentName { get; set; }
[DataType(DataType.Date)]
[Display(Name="Date to Post Payment")]
[Required(ErrorMessage = "Required")]
public DateTime PaymentDate { get; set; }
[DataType(DataType.Currency)]
[Range(0, 922337203685477.5807)]
[Required(ErrorMessage = "Required")]
public double PaymentAmount { get; set; }
}
MSDN Reference 1
MSDN Reference 2
Yes. Thats why you should use ViewModels.
You can, but don't have to, put those validation attributes in your model.
But it's better to use a ViewModel:
public class PaymentsViewModel
{
[DataType(DataType.Text)]
[DisplayFormat(NullDisplayText="")]
[Display(Name="Payment Id")]
[Required(ErrorMessage="Required")]
public int PaymentId { get; set; } //todo: make this into a dropdown
[DataType(DataType.Text)]
[Display(Name="Bill Name")]
[Required(ErrorMessage = "Required")]
public string PaymentName { get; set; }
[DataType(DataType.Date)]
[Display(Name="Date to Post Payment")]
[Required(ErrorMessage = "Required")]
public DateTime PaymentDate { get; set; }
[DataType(DataType.Currency)]
[Range(0, 922337203685477.5807)]
[Required(ErrorMessage = "Required")]
public double PaymentAmount { get; set; }
}
And in your View, instead of:
#model YourProject.Models.Payments
you use:
#model YourProject.Models.PaymentsViewModel
for validation.
Does it violate MVC in the strict sense, yeah, probably. Are there times when there's no harm in violating that? Sure. However, there are mechanisms to help you out and keep the concerns separate.
You can use your persisted domain objects in the views and validate against them, but when your view starts getting complicated, ViewModels become a necessity. For dead simple domain models or view-only views (not edits/creates), I'll sometimes fudge a bit and send them into the view as part of a composite object:
class MyViewModel
{
public MyDomainModel DomainObj;
public int OtherViewInfo;
}
However, for create and edit scenarios, ViewModels are far better. They allow you total control over the data being sent to the view and from the view.
If you are using EF 4.1 and CodeFirst, then yes, you'll end up with some duplication of attributes and properties between the domain and the ViewModel. This is unavoidable but gives you the flexibility of doing different validations specific to the view.
I've found it useful to have one extra layer of protection in the controller when actually saving the domain object in case I missed some validation in the view:
public class MyController : Controller
{
[HttpPost]
public ActionResult Edit(int id, MyViewModel model)
{
try
{
...Do stuff, check ModelState.IsValid...
_context.SaveChanges()
}
catch (DbEntityValidationException dbEx)
{
// Catch any validation errors on the domain that weren't duplicated
// in the viewmodel
ModelState.AddModelError("Form", dbEx);
}
return View(model);
}
}
The next question to ask is how you map between the domain model and the ViewModel. There are a number of strategies and tools out there - AutoMapper and ValueInjecter (yes, spelled wrong). Personally, I've been using ValueInjecter although if you setup a mapping layer, you can try both. I've found that neither will work in 100% of cases, or at least I could figure out how to do what I needed, but a mapping service makes it easy to define custom left-to-right maps.
I'm having some troubles with validation on my application.
Let's say I've the following models:
public class Company
{
public int id { get; set; }
[Required]
public String Name { get; set; }
public String Location { get; set; }
public List<Contacts> Contacts { get; set; }
}
public class Contact
{
public int id { get; set; }
[Required]
public String Name { get; set; }
[DataType(DataType.EmailAddress)]
public String Email { get; set; }
public String Telephone { get; set; }
public String Mobile { get; set; }
}
Now in my company create view I've two buttons, one to add contacts to the company, and another one to create the new company.
I detected which button was used in my controller like this (both buttons are named "button"):
[HttpPost]
public ActionResult Create(String button, FormCollection collection)
{
if(button == "AddContact")
{
AddContact(collection);
}
else
{
CreateCompany(collection);
}
}
While it's being created the object that represents the company that it's being create is stored in the session (for example HttpContext.Session["company"] = company;)
Now the problem is that if, for example, I try to add a contact without first specifying the company name, i get a validation error because the company name is required, which shouldn't happen because the user might want to add the contacts before adding the company info. Or if I try to save the company, I also get a validation error, because usually when saving the "add contact" form is empty, which means that the contact name (which is required as well) was not specified.
What I want to know is that if it's possible to validate the contact properties only when the addContact button is used, and validate the company properties only when the createCompany button is pressed.
For now i only need to do this serve-side, but if anyone has a solution to do this client-side as well i would appreciate the help.
You could trigger your own validation on the individual objects using
Validator.TryValidateObject(Object, ValidationContext, ICollection)
You can provide conditional validation using the Entity Framework by overriding DbEntityValidationResult in the DbContext. When this validation occurs in the DbContext you can access other entities. When validating a contact you can check the company too. For example:
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
ValidateContact(result);
return result;
}
private void ValidateContact(DbEntityValidationResult result)
{
var contact= result.Entry.Entity as Contact;
if (contact!= null && contact.ContactId != 0)
{
// Add validation code here, such as:
if(!string.IsNullOrEmpty(contact.Company.Name){
result.ValidationErrors.Add(
new DbValidationError(
"Contact",
"Company name cannot be null or empty when validating contacts.")
);
}
}
}
See Julia Lerman's Programming Entity Framework: DbContext http://www.amazon.com/Programming-Entity-Framework-Julia-Lerman/dp/1449312969 for more details.