1

我已經在自定義的DataAnnotationsModelMetadataProvider類中編寫了自定義錯誤消息本地化邏輯。使用內置的StringLengthAttribute或RequiredAttribute驗證錯誤消息,它工作得很好。但我有我的自定義派生RegularExpressionAttribute類的問題。我現在用的邏輯是類似下面的東西:Asp.Net MVC 3.0模型本地化與RegularExpression屬性

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

好像我的邏輯實際上是一個奇怪的做法。

我發現爲每種類型的驗證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; 
    } 
} 
0

你可以用我Griffin.MvcContrib獲得更容易定位。

  1. 使用的NuGet下載griffin.mvccontrib
  2. 定義字符串表描述here
  3. 直接在視圖模型中使用常規[RegularExpression]屬性。

添加到您的字符串表:

SignUpViewModel_AccountName_RegularExpression "{0} field must contain only letters, numbers or | . | - | _ | characters. 

就是這樣..

+0

我試過,但它並沒有爲我工作... http://stackoverflow.com/questions/ 23105025 /正則表達式-消息錯誤使用-格里芬-mvccontrib –