MVC3 / EF CustomValidator two fields in model - asp.net-mvc-3

Using MVC3 and EF4.1 how do I validate on client and server more than one field in my view model?
I have a start date text box (that can be modified) and I have the original start date in a hidden field. When the user submits the form I want to check that the modied start date is no more than one month either side of the original start date.
I can't figure out how this can be done with DataAnnotation and CustomValidation (or maybe I'm going down the wrong road)? This is an example of whay I've been working with:
[MetadataType(typeof(Metadata.MyUserMetaData))]
public partial class MyUser
{
public System.DateTime DateOfBirth { get; set; }
}
Partial Class
public class MyUserMetaData
{
[CustomValidation(typeof(AmendedStartDate), "amendedstartdate", ErrorMessage = "Invalid date."]
public DateTime StartDate { get; set; };
public DateTime OriginalStartDate { get; set; };
}
Custom Validator
public class AmendedStartDate : ValidationAttribute, IClientValidatable
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// How do I get multiple field values from object value?
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(Modelmetadata metadate, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "amendedstartdate"
};
yield return rule;
}
}
I know I've still to add jQuery to the view for this validator.

Instead of using data annotations implement IValidatableObject on your model class - it is simpler and much more clear in scenarios with cross validation.
If you still want to use ValidationAttribute you have two parameters in the IsValid method:
value represents validated value of the property where the attribute is assigned
context is context in which the property is validated. It also contains ObjectInstance and ObjectType properties to access the whole model and its type so you can cast the instance and access other properties.

The question asked in MVC custom validation: compare two dates has an example of a validator which compares to a second value in the model. That should get you started.

Related

Custom Validation For Required fields

Hi I would want to have validations something of this sort
[RequiredCustom(ActionType=(int)Action.Update, ActionType=(int)Action.Delete)]
public string NotesID { get; set; }
[Required]
[RegularExpression("1|2|3|4")]
public int ActionType { get; set; }
I would want to validate this NotesID only when Updation and Deletion is taking place. I don't need any javascript code for unobtrusive and all. I just want server side validation.
Please don't suggest use of separate models I can't do that. Something similar solution will also do.
Let me know if this requires more clarification, Any help would be greatly appreciated.
If you are ok with placing the validation data annotations on the class itself, you could do something like the answer to this post (by Gary.S) suggests.
Here
In essense, just create a new custom validation attribute and since its an attribute at class level, you will have access to the object itself and hence access to other properties.
You custom attribute should look something like this (Assuming your class name is 'Notes')
public class RequiredCustom: ValidationAttribute
{
List<int> actions;
public RequiredCustom(int[] actions)
{
this.actions = new List<int>();
this.actions.AddRange(actions);
}
public override bool IsValid(object value)
{
bool isValid = true;
Notes testVal = value as Notes;
if(this.actions.Contains((int)testVal.Action))
{
if(string.IsNullOrWhiteSpace(testVal.NotesID))
{
isValid = false;
}
}
return isValid;
}
}
Then, add the validation attribute on the class and send the array of enums you want to test for. This way it will be dynamic enough to send other later. In this case, I am sending an ARRAY of Update and Delete enums since you have a special case for these two.
[RequiredCustom(new int[] {(int)Action.Update, (int)Action.Delete)}]
public class Notes
{
public string NotesID { get; set; }
[Required]
[RegularExpression("1|2|3|4")]
public int ActionType { get; set; }
}
Remember, this will work only if you set the attribute on the class, not on the NotesId property.

Is it possible to force the currency for an MVC3 field with DataType as DataType.Currency

I'm writing an MVC3 application that reads in a bunch of monetary data from a database. The issue I have is that these amounts are all in different currencies.
If I set the type of a field like this:
[DataType(DataType.Currency)]
public Amount{ get; set;}
I get the decimal places and a currency symbol, which looks nice, but it defaults to the user's local currency. A US user sees $423.29 whereas a GB user sees £423.29. I can override the currency by using a <globalization culture="{something}"> in the Web.config, but this sets all currency fields globally.
What would be the easiest way of marking up a field so that it renders with the correct decimal places and currency symbol?
In an ideal world, I'd like to be able to do something like this (for USD):
[DataType(DataType.Currency, culture="en-us")]
public Amount{ get; set; }
and have that always render as $439.38, but that's not possible with the built-in annotations.
The way I would do this is to create a custom attribute that extends the DataType attribute and a custom html helper. It's not necessarily the easiest way of doing it but it would save time in the future.
EDIT
Incorporated CultureInfo.CreateSpecificCulture(cultureName) instead of a switch
Custom Attribute
public class CurrencyDisplayAttribute : DataTypeAttribute
{
public string Culture { get; set; }
public CurrencyDisplayAttribute(string culture)
: base(DataType.Currency)
{
Culture = culture;
}
}
Html Helper
public static class Helpers
{
public static IHtmlString CurrencyDisplayFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
double value = double.Parse(expression.Compile().Invoke(helper.ViewData.Model).ToString());
var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
var prop = typeof (TModel).GetProperty(metadata.PropertyName);
var attribute = prop.GetCustomAttribute(typeof (CurrencyDisplayAttribute)) as CurrencyDisplayAttribute;
// this should be whatever html element you want to create
TagBuilder tagBuilder = new TagBuilder("span");
tagBuilder.SetInnerText(value.ToString("c", CultureInfo.CreateSpecificCulture(attribute.Culture));
return MvcHtmlString.Create(tagBuilder.ToString());
}
}
You can use the attribute in your model
[CurrencyDisplay("en-us")]
public double Amount { get; set; }
Then in your view you can use the helper by
#Html.CurrencyDisplayFor(x => x.Amount);
Provided your model is passed in correctly.
Obviously, you'd need to do error checking and so on.
Make string the amount
public string Amount{ get; set;}
Create a method that converts to string with currency exact
private string LocalizedAmount(decimal theAmount, string cultureName)
{
return theAmount.ToString("c",CultureInfo.CreateSpecificCulture(cultureName));
}
If you are stored in your database two fields or columns one for value and one for culture.
And in the Repository or the controller:
Amount = LocalizedAmount(Convert.ToDecimal(reader[0]),reader[1].ToString());
You need to assing the culture:
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
Inside this Controller overrided methods:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
protected override void ExecuteCore()
Read the whole technique in this NadeemAfana's blog ASP.NET MVC 3 Internationalization

MVC 3: Use a VERY custom format for a Date with automapping on server-side

I have a very confusing problem, and I am able to solve it, but there has to be a Out-Of-The-Box-Solution.
The situation is as following:
An user inputs on the client-side a date (i.e. with a date-picker) which outputs the following value to the textbox:
Mi 22.02.2012 (KW 8)
I have a Button which posts my form with this textbox to an controller-action.
Let's say you have the following controller action which takes a MyType as Parameter
[HttpPost]
public ActionResult Create(MyType model)
{
//model.Date is always null
//(or in case the DateTime is not Nullable<T>, the value is DateTime.Min
}
public class MyType
{
public DateTime? Date { get; set; }
}
The question is simple:
How do I map the input to the DateTime?
I do not want to add properties to my class like in the following example:
public class IDontWantThis
{
public DateTime? Date { get { /* Some custom string parsing with DateText */ } }
public string DateText { get; set; } //this would be the auto-mapped property
}
How a date is displayed in the view is of no concern to the controller.
I would add a hidden field with the name of the property you want the datetime to map on the viewmodel (Date). On the client, using javascript, you can format this date any way you want, as long as the hidden field contains the datetime you need to work with.
So let the datepicker store the selected date twice, once with your weird date formatting for display and another time in a hidden field in a common format the modelbinder will understand.
This is what ViewModels are for. I understand that you don't want your domain Model to include a DateText member, but you really should consider something like this:
public class MyType
{
public DateTime? Date { get; set; }
}
public class MyTypeViewModel
{
public MyType MyType { get; set; }
public string DateText { get; set; }
}
What your requirement is one of the main points that makes ViewModels so powerful. Using an intermediate class between your View and your Model to link them together. This way, you won't have any trace of "formatting" data in your Model (MyType).

ASP.NET - MVC3 property decoration?

Is there an attribute I can decorate a single property on my model to tell the engine not to include the property in the validation routine?
[DoNotValidate] or [ValidateIgnore]
----------------------More info.
Ok, I need to give you more information. In my situation, I have a temporary decimal value on my model that is not persisted, that gets formatted into currency. $540,000.
In this one case I do not want to strip the formatting out before I call TryUpdateModel. When you use TryupdateModel, it mvc will try and convert that string text box value back into a decimal and Model.IsValid will return false. I know how to get around this situation, using javascript, but it would be easier if I could tell mvc not to validate that field.
Any model properties not decorated with validation attributes should be ignored.
public class MyModel
{
[Required]
public string SomeProperty { get; set; }
public string IgnoredProperty { get; set; }
}
Should validate that SomeProperty is required, but nothing will happen with IgnoredProperty.
The best tutorial IMHO on Model validation is http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
(even though it says for MVC 2, it's applicable).
Change the type of your decimal to nullable decimal to prevent required validation:
public class MyModel
{
public decimal MyValidatingDecimal { get; set; }
public decimal? MyNonValidatingDecimal { get; set; }
}
MyValidatingDecimal will be required (since it is a value-type), while MyNonValidatingDecimal will not be required.
Properties will only be validated if you explicitly apply validation attributes to them.

Custom model validation of dependent properties using Data Annotations

Since now I've used the excellent FluentValidation
library to validate my model classes. In web applications I use it in conjunction with the jquery.validate plugin to perform client side validation as well.
One drawback is that much of the validation logic is repeated on the client side and is no longer centralized at a single place.
For this reason I'm looking for an alternative. There are many examples out there showing the usage of data annotations to perform model validation. It looks very promising.
One thing I couldn't find out is how to validate a property that depends on another property value.
Let's take for example the following model:
public class Event
{
[Required]
public DateTime? StartDate { get; set; }
[Required]
public DateTime? EndDate { get; set; }
}
I would like to ensure that EndDate is greater than StartDate. I could write a custom
validation attribute extending ValidationAttribute in order to perform custom validation logic. Unfortunately I couldn't find a way to obtain the
model instance:
public class CustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// value represents the property value on which this attribute is applied
// but how to obtain the object instance to which this property belongs?
return true;
}
}
I found that the CustomValidationAttribute seems to do the job because it has this ValidationContext property that contains the object instance being validated. Unfortunately this attribute has been added only in .NET 4.0. So my question is: can I achieve the same functionality in .NET 3.5 SP1?
UPDATE:
It seems that FluentValidation already supports clientside validation and metadata in ASP.NET MVC 2.
Still it would be good to know though if data annotations could be used to validate dependent properties.
MVC2 comes with a sample "PropertiesMustMatchAttribute" that shows how to get DataAnnotations to work for you and it should work in both .NET 3.5 and .NET 4.0. That sample code looks like this:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
private readonly object _typeId = new object();
public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
: base(_defaultErrorMessage)
{
OriginalProperty = originalProperty;
ConfirmProperty = confirmProperty;
}
public string ConfirmProperty
{
get;
private set;
}
public string OriginalProperty
{
get;
private set;
}
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
OriginalProperty, ConfirmProperty);
}
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
return Object.Equals(originalValue, confirmValue);
}
}
When you use that attribute, rather than put it on a property of your model class, you put it on the class itself:
[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public class ChangePasswordModel
{
public string NewPassword { get; set; }
public string ConfirmPassword { get; set; }
}
When "IsValid" gets called on your custom attribute, the whole model instance is passed to it so you can get the dependent property values that way. You could easily follow this pattern to create a date comparison attribute, or even a more general comparison attribute.
Brad Wilson has a good example on his blog showing how to add the client-side portion of the validation as well, though I'm not sure if that example will work in both .NET 3.5 and .NET 4.0.
I had this very problem and recently open sourced my solution:
http://foolproof.codeplex.com/
Foolproof's solution to the example above would be:
public class Event
{
[Required]
public DateTime? StartDate { get; set; }
[Required]
[GreaterThan("StartDate")]
public DateTime? EndDate { get; set; }
}
Instead of the PropertiesMustMatch the CompareAttribute that can be used in MVC3. According to this link http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1:
public class RegisterModel
{
// skipped
[Required]
[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
public string ConfirmPassword { get; set; }
}
CompareAttribute is a new, very useful validator that is not actually
part of
System.ComponentModel.DataAnnotations,
but has been added to the
System.Web.Mvc DLL by the team. Whilst
not particularly well named (the only
comparison it makes is to check for
equality, so perhaps EqualTo would be
more obvious), it is easy to see from
the usage that this validator checks
that the value of one property equals
the value of another property. You can
see from the code, that the attribute
takes in a string property which is
the name of the other property that
you are comparing. The classic usage
of this type of validator is what we
are using it for here: password
confirmation.
It took a little while since your question was asked, but if you still like metadata (at least sometimes), below there is yet another alternative solution, which allows you provide various logical expressions to the attributes:
[Required]
public DateTime? StartDate { get; set; }
[Required]
[AssertThat("StartDate != null && EndDate > StartDate")]
public DateTime? EndDate { get; set; }
It works for server as well as for client side. More details can be found here.
Because the methods of the DataAnnotations of .NET 3.5 don't allow you to supply the actual object validated or a validation context, you will have to do a bit of trickery to accomplish this. I must admit I'm not familiar with ASP.NET MVC, so I can't say how to do this exactly in conjunction with MCV, but you can try using a thread-static value to pass the argument itself. Here is an example with something that might work.
First create some sort of 'object scope' that allows you to pass objects around without having to pass them through the call stack:
public sealed class ContextScope : IDisposable
{
[ThreadStatic]
private static object currentContext;
public ContextScope(object context)
{
currentContext = context;
}
public static object CurrentContext
{
get { return context; }
}
public void Dispose()
{
currentContext = null;
}
}
Next, create your validator to use the ContextScope:
public class CustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
Event e = (Event)ObjectContext.CurrentContext;
// validate event here.
}
}
And last but not least, ensure that the object is past around through the ContextScope:
Event eventToValidate = [....];
using (var scope new ContextScope(eventToValidate))
{
DataAnnotations.Validator.Validate(eventToValidate);
}
Is this useful?

Resources