{Version 8.0.0}
为什么这个测试会通过?
测试:
[Test]
public void Validation_NullTo_ShouldThrowModelValidationException()
{
var config = new EmailMessage
{
Subject = "My Subject",
Body = "My Body",
To = null
};
EmailMessageValidator validator = new EmailMessageValidator();
ValidationResult results = validator.Validate(config);
if (!results.IsValid)
{
foreach (var failure in results.Errors)
{
Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
}
}
// results.Errors is Empty and resuts.IsValid is true here. Should be false with at least one Error message.
Assert.True(results.IsValid);
}
如果我这样更改消息的结构,验证通常会失败。
var config = new EmailMessage
{
Subject = "My Subject",
Body = "My Body"
};
config.To = new EmailAddressList();
验证者:
public class EmailAddressListAtLeastOneRequiredValidator : AbstractValidator<EmailAddressList>
{
public EmailAddressListAtLeastOneRequiredValidator()
{
RuleFor(model => model)
.NotNull()
.WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));
RuleFor(model => model.Value)
.NotNull()
.WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));
RuleFor(model => model.Value.Count)
.GreaterThan(0)
.WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));
When(model => model.Value?.Count > 0, () =>
{
RuleFor(model => model.Value)
.NotEmpty()
.WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));
});
}
}
public class EmailAddressListValidator : AbstractValidator<EmailAddressList>
{
public EmailAddressListValidator()
{
RuleFor(model => model.Value).SetCollectionValidator(new EmailAddressValidator());
}
}
public class EmailAddressValidator : AbstractValidator<EmailAddress>
{
public EmailAddressValidator()
{
When(model => model.Value != null, () =>
{
RuleFor(model => model.Value)
.EmailAddress()
.WithMessage(AppMessages.Validation.ValueCannotBeNullOrEmpty.ParseIn(nameof(EmailAddress)));
});
}
}
public class EmailMessageValidator : AbstractValidator<EmailMessage>
{
public EmailMessageValidator()
{
RuleFor(model => model.To).SetValidator(new EmailAddressListAtLeastOneRequiredValidator());
When(model => model.Cc?.Value?.Count > 0, () =>
{
RuleFor(model => model.Cc).SetValidator(new EmailAddressListValidator());
});
When(model => model.Bcc?.Value?.Count > 0, () =>
{
RuleFor(model => model.Bcc).SetValidator(new EmailAddressListValidator());
});
RuleFor(model => model.Subject)
.NotEmpty().WithMessage(AppMessages.Validation.ValueCannotBeNullOrEmpty.ParseIn(nameof(EmailMessage.Subject)))
.MaximumLength(100).WithMessage(AppMessages.Validation.ValueLengthCannotBeGreaterThan.ParseIn(nameof(EmailMessage.Subject), 100));
RuleFor(model => model.Body)
.NotEmpty().WithMessage(AppMessages.Validation.ValueCannotBeNullOrEmpty.ParseIn(nameof(EmailMessage.Body)));
}
}
EmailMessage和EmailAddressList类:
public class EmailMessage : IEmailMessage
{
public EmailAddressList To { get; set; } = new EmailAddressList();
public EmailAddressList Cc { get; set; } = new EmailAddressList();
public EmailAddressList Bcc { get; set; } = new EmailAddressList();
public string Subject { get; set; }
public string Body { get; set; }
}
public class EmailAddressList : ModelValidation, IEnumerable<EmailAddress>
{
public List<EmailAddress> Value { get; set; } = new List<EmailAddress>();
public EmailAddressList()
: base(new EmailAddressListValidator())
{
}
public EmailAddressList(string emailAddressList)
: base(new EmailAddressListValidator())
{
Value = Split(emailAddressList);
}
public EmailAddressList(IValidator validator)
: base(validator ?? new EmailAddressListValidator())
{
}
public List<EmailAddress> Split(string emailAddressList, char splitChar = ';')
{
return emailAddressList.Contains(splitChar)
? emailAddressList.Split(splitChar).Select(email => new EmailAddress(email)).ToList()
: new List<EmailAddress> { new EmailAddress(emailAddressList) };
}
public string ToString(char splitChar = ';')
{
if (Value == null)
return "";
var value = new StringBuilder();
foreach (var item in Value)
value.Append($"{item.Value};");
return value.ToString().TrimEnd(';');
}
public void Add(string emailAddress, string displayName = "")
{
Value.Add(new EmailAddress(emailAddress, displayName));
}
public void Add(EmailAddress emailAddress)
{
Value.Add(emailAddress);
}
public IEnumerator<EmailAddress> GetEnumerator()
{
return Value.GetEnumerator();
}
[ExcludeFromCodeCoverage]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
答案 0 :(得分:0)
这是杰里米·斯金纳的答案...
此行为是正常且正确的。复杂的儿童验证器集 仅当目标属性不是 空值。由于To属性为null,因此子验证器不会 调用。您应该做的是将SetValidator与 NotNull检查:
RuleFor(model => model.To).NotNull()。WithMessage(“ ...”);
.SetValidator(new EmailAddressListAtLeastOneRequiredValidator());...然后从中删除RuleFor(model => model).NotNull() EmailAddressListAtLeastOneRequiredValidator,因为它将永远不会 被执行。覆盖PreValidate与此处无关。使用 这样的PreValidate仅在尝试将null传递给 根验证器。与儿童验证器一起工作时,他们永远不能 使用null实例(这是设计使然)调用的,并且null 应该由父验证器处理。