我在自定义DataAnnotationsModelMetadataProvider类中编写了自定义错误消息本地化逻辑。它与内置的StringLengthAttribute或RequiredAttribute验证错误消息一起正常工作。但是我的自定义派生的RegularExpressionAttribute类遇到了问题。我使用的逻辑如下所示:
public class AccountNameFormatAttribute : RegularExpressionAttribute {
public AccountNameFormatAttribute()
: base(Linnet.Core.Shared.RegExPatterns.AccountNamePattern) {
}
public override string FormatErrorMessage(string name) {
return string.Format("{0} field must contain only letters, numbers or | . | - | _ | characters.", name);
}
}
public class SignUpViewModel {
[AccountNameFormat()]
[StringLength(16, MinimumLength = 3)]
[Required]
[DisplayName("Account Name")]
public string AccountName { get; set; }
[Required]
[DisplayName("Password")]
[StringLength(32, MinimumLength = 6)]
[DataType(System.ComponentModel.DataAnnotations.DataType.Password)]
public string Password { get; set; }
// .... and other properties, quite similar ... //
}
public class MvcDataAnnotationsModelValidatorProvider : DataAnnotationsModelValidatorProvider {
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) {
MyMvcController myMvcController = context.Controller as MyMvcController; /* custom mvc controller, that contains methods for wcf service activations and common properties. */
if (myMvcController == null) {
return base.GetValidators(metadata, context, attributes);
}
List<Attribute> newAttributes = new List<Attribute>();
foreach (Attribute att in attributes) {
if (att.GetType() != typeof(ValidationAttribute) && !att.GetType().IsSubclassOf(typeof(ValidationAttribute))) {
// if this is not a validation attribute, do nothing.
newAttributes.Add(att);
continue;
}
ValidationAttribute validationAtt = att as ValidationAttribute;
if (!string.IsNullOrWhiteSpace(validationAtt.ErrorMessageResourceName) && validationAtt.ErrorMessageResourceType != null) {
// if resource key and resource type is already set, do nothing.
newAttributes.Add(validationAtt);
continue;
}
string translationKey = "MvcModelMetaData.ValidationMessages." + metadata.ModelType.Name + (metadata.PropertyName != null ? "." + metadata.PropertyName : string.Empty) + "." + validationAtt.GetType().Name;
string originalText = validationAtt.FormatErrorMessage("{0}"); /* non-translated default english text */
// clonning current attiribute into a new attribute
// not to ruin original attribute for later usage
// using Activator.CreateInstance and then mapping with AutoMapper inside..
var newAtt = this.CloneValidationAttiribute(validationAtt);
// fetching translation from database via WCF service...
// At this point, i can see error strings are always translated.
// And it works perfect with [Required], [StringLength] and [DataType] attributes.
// But somehow it does not work with my AccountNameFormatAttribute on the web page, even if i give it the translated text as expected..
// Even if its ErrorMessage is already set to translated text,
// it still displays the original english text from the overridden FormatErrorMessage() method on the web page.
// It is the same both with client side validation or server side validation.
// Seems like it does not care the ErrorMessage that i manually set.
newAtt.ErrorMessage = myMvcController.Translations.GetTranslation(translationKey, originalText);
newAttributes.Add(newAtt);
}
IEnumerable<ModelValidator> result = base.GetValidators(metadata, context, newAttributes);
return result;
}
private ValidationAttribute CloneValidationAttiribute(ValidationAttribute att) {
if (att == null) {
return null;
}
Type attType = att.GetType();
ConstructorInfo[] constructorInfos = attType.GetConstructors();
if (constructorInfos == null || constructorInfos.Length <= 0) {
// can not close..
return att;
}
if (constructorInfos.Any(ci => ci.GetParameters().Length <= 0)) {
// clone with no constructor paramters.
return CloneManager.CloneObject(att) as ValidationAttribute;
}
// Validation attributes that needs constructor paramters...
if (attType == typeof(StringLengthAttribute)) {
int maxLength = ((StringLengthAttribute)att).MaximumLength;
return CloneManager.CloneObject(att, maxLength) as StringLengthAttribute;
}
return att;
}
}
public class CloneManager {
public static object CloneObject(object input) {
return CloneObject(input, null);
}
public static object CloneObject(object input, params object[] constructorParameters) {
if (input == null) {
return null;
}
Type type = input.GetType();
if (type.IsValueType) {
return input;
}
ConstructorInfo[] constructorInfos = type.GetConstructors();
if (constructorInfos == null || constructorInfos.Length <= 0) {
throw new LinnetException("0b59079b-3dc4-4763-b26d-651bde93ba56", "Object type does not have any constructors.", false);
}
if ((constructorParameters == null || constructorParameters.Length <= 0) && !constructorInfos.Any(ci => ci.GetParameters().Length <= 0)) {
throw new LinnetException("f03be2b9-b629-4a72-b025-c7a87924d9a4", "Object type does not have any constructor without parameters.", false);
}
object newObject = null;
if (constructorParameters == null || constructorParameters.Length <= 0) {
newObject = Activator.CreateInstance(type);
} else {
newObject = Activator.CreateInstance(type, constructorParameters);
}
return MapProperties(input, newObject);
}
private static object MapProperties(object source, object destination) {
if (source == null) {
return null;
}
Type type = source.GetType();
if (type != destination.GetType()) {
throw new LinnetException("e67bccfb-235f-42fc-b6b9-55f454c705a8", "Use 'MapProperties' method only for object with same types.", false);
}
if (type.IsValueType) {
return source;
}
var typeMap = AutoMapper.Mapper.FindTypeMapFor(type, type);
if (typeMap == null) {
AutoMapper.Mapper.CreateMap(type, type);
}
AutoMapper.Mapper.Map(source, destination, type, type);
return destination;
}
}
答案 0 :(得分:0)
似乎我的逻辑实际上是一种奇怪的方法。
我发现为每种类型的验证attiributes制作自定义DataAnnotationsModelValidators。然后在Validate()和GetClientValidationRules()方法中翻译ErrorMessages。
public class MvcRegularExpressionAttributeAdapter : RegularExpressionAttributeAdapter {
public MvcRegularExpressionAttributeAdapter(ModelMetadata metadata, ControllerContext context, RegularExpressionAttribute attribute)
: base(metadata, context, attribute) {
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
return MvcValidationResultsTranslation.TranslateClientValidationRules(base.GetClientValidationRules(), this.Metadata, this.ControllerContext, this.Attribute);
}
public override IEnumerable<ModelValidationResult> Validate(object container) {
return MvcValidationResultsTranslation.TranslateValidationResults(base.Validate(container), this.Metadata, this.ControllerContext, this.Attribute);
}
}
public class MvcValidationResultsTranslation {
public static IEnumerable<ModelClientValidationRule> TranslateClientValidationRules(IEnumerable<ModelClientValidationRule> validationRules, ModelMetadata metadata, ControllerContext context, ValidationAttribute validationAttribute) {
if (validationRules == null) {
return validationRules;
}
MvcController mvcController = context.Controller as MvcController;
if (mvcController == null) {
return validationRules;
}
if (!string.IsNullOrWhiteSpace(validationAttribute.ErrorMessageResourceName) && validationAttribute.ErrorMessageResourceType != null) {
// if resource key and resource type is set, do not override..
return validationRules;
}
string translatedText = GetTranslation(metadata, mvcController, validationAttribute);
foreach (var validationRule in validationRules) {
List<string> msgParams = new List<string>();
msgParams.Add(!string.IsNullOrEmpty(metadata.DisplayName) ? metadata.DisplayName : metadata.PropertyName);
if (validationRule.ValidationParameters != null) {
msgParams.AddRange(validationRule.ValidationParameters.Where(p => p.Value.GetType().IsValueType || p.Value.GetType().IsEnum).Select(p => p.Value.ToString()));
}
validationRule.ErrorMessage = string.Format(translatedText, msgParams.ToArray());
}
return validationRules;
}
public static IEnumerable<ModelValidationResult> TranslateValidationResults(IEnumerable<ModelValidationResult> validationResults, ModelMetadata metadata, ControllerContext context, ValidationAttribute validationAttribute) {
if (validationResults == null) {
return validationResults;
}
MvcController mvcController = context.Controller as MvcController;
if (mvcController == null) {
return validationResults;
}
if (!string.IsNullOrWhiteSpace(validationAttribute.ErrorMessageResourceName) && validationAttribute.ErrorMessageResourceType != null) {
// if resource key and resource type is set, do not override..
return validationResults;
}
string translatedText = GetTranslation(metadata, mvcController, validationAttribute);
List<ModelValidationResult> newValidationResults = new List<ModelValidationResult>();
foreach (var validationResult in validationResults) {
ModelValidationResult newValidationResult = new ModelValidationResult();
newValidationResult.Message = string.Format(translatedText, (!string.IsNullOrEmpty(metadata.DisplayName) ? metadata.DisplayName : metadata.PropertyName));
newValidationResults.Add(newValidationResult);
}
return newValidationResults;
}
}
答案 1 :(得分:0)
您可以使用我的Griffin.MvcContrib来更轻松地进行本地化。
griffin.mvccontrib
[RegularExpression]
属性。将其添加到字符串表中:
SignUpViewModel_AccountName_RegularExpression "{0} field must contain only letters, numbers or | . | - | _ | characters.
就是这样..