Entity Framework Complex Type Custom Validation, Stopping Validation Recursion
We are using complex types to manage our translatable fields like this:
[ComplexType]
public class Translated
{
开发者_Go百科 [Required]
public string NL { get; set; }
[Required]
public string EN { get; set; }
[ScaffoldColumn(false)]
public string TranslatedText
{
get
{
return Util.Translate(NL, EN);
}
}
}
We require the fields to be present. But in some cases the whole Translated field is optional as in:
public class Question
{
...
[Optional(ErrorMessage="Foo")]
public Translated Description { get; set; }
...
}
However, it seems that the Optional attribute gets calculated, and when it returns false nothing is done with the result.
class OptionalAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
return false;
}
}
When I put the optional attribute on a non-complex type it works as expected, the error message will always be Foo.
The ultimate goal is to allow the Description to be empty in both cases, but when one of the fields is filled the errors should of course propagate.
Stopping the validation recursion would cause the field to be optional, but it would also prevent the validation of the fields in case they are filled in.
Any ideas on how to accomplish this?
Using the data annotation [Required]
on your string properties will create non nullable fields in the database. From your description it seems that sometimes you'll want both of those values to be null.
I would suggest implementing your own validation defining what makes those fields optional.
[ComplexType]
public class Translated : IValidatableObject
{
public string NL { get; set; }
public string EN { get; set; }
[NotMapped]
public string TranslatedText
{
get
{
return Util.Translate(NL, EN);
}
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrEmpty(NL) && string.IsNullOrEmpty(EN))
yield return new ValidationResult("EN is required if NL is entered.");
if (!string.IsNullOrEmpty(EN) && string.IsNullOrEmpty(NL))
yield return new ValidationResult("NL is required if EN is entered.");
}
}
This is what I'm doing now. It has the disadvantage that for each kind of Translated (Translated, TranslatedOptional, TranslatedMultiline and TranslatedMultilineOptional) you have seperate classes.
Another disadvantage is that I don't know how to add the validation results to the NL en EN fields themselves instead of to the Translated.
[ComplexType]
public class TranslatedMultiline : IValidatableObject
{
[DataType(DataType.MultilineText)]
[Display(Name = "dutch", ResourceType = typeof(Caracal.Resources.GUI))]
public string NL { get; set; }
[DataType(DataType.MultilineText)]
[Display(Name = "english", ResourceType = typeof(Caracal.Resources.GUI))]
public string EN { get; set; }
[ScaffoldColumn(false)]
public string TranslatedText
{
get
{
return Util.Translate(NL, EN);
}
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (this is IOptional)
{
if (!string.IsNullOrEmpty(NL) && string.IsNullOrEmpty(EN))
yield return new ValidationResult("EN is required if NL is entered.");
// TODO: Translate
if (!string.IsNullOrEmpty(EN) && string.IsNullOrEmpty(NL))
yield return new ValidationResult("NL is required if EN is entered.");
// TODO: Translate
}
else
{
if (string.IsNullOrEmpty(NL))
yield return new ValidationResult("NL is required");
if (string.IsNullOrEmpty(EN))
yield return new ValidationResult("EN is required");
}
}
}
[ComplexType]
public class TranslatedMultilineOptional : TranslatedMultiline, IOptional { }
public interface IOptional {}
精彩评论