ASP .NET MVC 5で確認。
入れ子のModelのValidationは効かない。
Attachmentの中のNameAndExtensionとかのRequiredはValidationされない。
public class Email
{
[Required]
public string From { get; set; }
[Required]
public string Subject { get; set; }
public string Body { get; set; }
[Required]
public EmailAttachment Attachment { get; set; }
public Email()
{
}
public class EmailAttachment
{
[Required]
public string NameAndExtension { get; set; }
[Required]
public byte[] File { get; set; }
}
}
子のValidationを行うためのAttributeを作成する
/// <summary>
/// 子オブジェクトのValidationも行います
/// </summary>
public class NestedValidateAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null) return ValidationResult.Success;
var results = new List<ValidationResult>();
var context = new ValidationContext(value, null, null);
//Validationを実施
Validator.TryValidateObject(value, context, results, true);
if (results.Count != 0)
{
var validationResults = results.Select(x => new ValidationResult(x.ErrorMessage, x.MemberNames.Select(y => $"{validationContext.DisplayName}.{y}")));
var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", new { validationContext.DisplayName }));
compositeResults.AddResults(validationResults);
return compositeResults;
}
return ValidationResult.Success;
}
}
/// <summary>
/// 複数のValidation結果を格納できる<see cref="ValidationResult" />
/// </summary>
public class CompositeValidationResult : ValidationResult
{
/// <summary>
/// 複数のValidation結果
/// </summary>
public IEnumerable<ValidationResult> Results { get;} = new List<ValidationResult>();
public CompositeValidationResult(string errorMessage) : base(errorMessage) { }
public CompositeValidationResult(string errorMessage, IEnumerable<string> memberNames) : base(errorMessage, memberNames) { }
protected CompositeValidationResult(ValidationResult validationResult) : base(validationResult) { }
/// <summary>
/// エラーを登録
/// </summary>
/// <param name="validationResult"></param>
public void AddResult(ValidationResult validationResult)
{
(Results as IList<ValidationResult>).Add(validationResult);
}
/// <summary>
/// エラーを登録
/// </summary>
/// <param name="validationResults"></param>
public void AddResults(IEnumerable<ValidationResult> validationResults)
{
(Results as List<ValidationResult>).AddRange(validationResults);
}
}
使うときはこんな感じでAttachmentにNestedValidate属性をつければよい。
public class Email
{
[Required]
public string From { get; set; }
[Required]
public string Subject { get; set; }
public string Body { get; set; }
[Required]
[NestedValidate]
public EmailAttachment Attachment { get; set; }
public Email()
{
}
public class EmailAttachment
{
[Required]
public string NameAndExtension { get; set; }
[Required]
public byte[] File { get; set; }
}
}
Controller内で確認.
ModelState内にValidation結果が格納されている。
public ActionResult Create(Email viewModel)
{
var errors = ModelState
.Where(x => x.Value.Errors.Count > 0)
.Select(y =>
new
{
MemberName = y.Key,
ErrorMessages = y.Value.Errors.Select(e => e.ErrorMessage).ToArray(),
Exception = y.Value.Errors.Select(e => e.Exception).FirstOrDefault()?.ToString()
});
/*以下略*/
}