2009-12-04 11 views
19

Ich habe eine benutzerdefinierte ResourceProvider erstellt, um Lokalisierungsinformationen aus einer Datenbank zu ziehen. Ich möchte nun DataAnnotation verwenden, um das Modell zu validieren.DataAnnotation mit benutzerdefinierten ResourceProvider

DataAnnotation hat ErrorMessageResourceType und ErrorMessageResourceName Eigenschaften aber ErrorMessageResourceType akzeptiert nur System.Type (das heißt eine kompilierte Ressource-Datei)

Gibt es eine Möglichkeit DataAnnotation zu bekommen die benutzerdefinierten ResourceProvider zu benutzen?

Antwort

5

Ich weiß, das ist eine alte Frage, aber wollte ein bisschen hinzufügen. Ich befand mich in der gleichen Situation und es scheint keine Dokumentation/Blogging zu diesem Thema zu geben. Dennoch habe ich einen Weg gefunden, einen benutzerdefinierten Ressourcenanbieter mit einem Vorbehalt zu verwenden. Der Vorbehalt ist, dass ich in einer MVC-Anwendung bin, so dass ich immer noch HttpContext.GetLocalResourceObject() zur Verfügung habe. Dies ist die Methode, mit der asp.net Elemente lokalisiert. Die Abwesenheit des Ressourcenobjekts hindert Sie nicht daran, unsere eigene Lösung zu schreiben, selbst wenn es sich um eine direkte Abfrage der DB-Tabellen handelt. Trotzdem dachte ich, dass es sich lohnt, darauf hinzuweisen.

Während ich mit der folgenden Lösung nicht sehr glücklich bin, scheint es zu funktionieren. Für jedes Validierungsattribut, das ich verwenden möchte, erben wir von diesem Attribut und überlasten das IsValid(). Die Dekoration sieht wie folgt aus:

public sealed class RequiredLocalized : RequiredAttribute { 

    public override bool IsValid(object value) { 

     if (! (ErrorMessageResourceType == null || String.IsNullOrWhiteSpace(ErrorMessageResourceName)) ) { 
      this.ErrorMessage = MVC_HtmlHelpers.Localize(this.ErrorMessageResourceType, this.ErrorMessageResourceName); 
      this.ErrorMessageResourceType = null; 
      this.ErrorMessageResourceName = null; 
     } 
     return base.IsValid(value); 
    } 
} 

Hinweise

  • Sie müssen Ihren Code mit dem abgeleiteten Attribut schmücken, nicht der Standard:

    [RequiredLocalized(ErrorMessageResourceType= typeof(ClassBeginValidated), ErrorMessageResourceName="Errors.GenderRequired")] 
    public string FirstName { get; set; } 
    

    Das neue Attribut wie folgt aussieht Eine

  • Ich benutze ErrorMessageResourceType, um den Typ der zu validierenden Klasse zu übergeben. Damit meine ich, wenn ich in einer Kundenklasse bin und die FirstName Eigenschaft validiere, würde ich typeof (Kunde) übergeben. Ich mache das, weil ich in meinem Datenbank-Backend den vollständigen Klassennamen (Namespace + Klassenname) als Schlüssel verwende (genauso wie eine Seiten-URL in asp.net verwendet wird).
    • MVC_HtmlHelpers.Localize ist nur ein einfaches Wrapper für meinen benutzerdefinierten Ressource-Anbieter

Der (halb-gestohlen) Helfer Code wie folgt aussieht ....

public static string Localize (System.Type theType, string resourceKey) { 
    return Localize (theType, resourceKey, null); 
} 
public static string Localize (System.Type theType, string resourceKey, params object[] args) { 
    string resource = (HttpContext.GetLocalResourceObject(theType.FullName, resourceKey) ?? string.Empty).ToString(); 
    return mergeTokens(resource, args); 
} 

private static string mergeTokens(string resource, object[] args)  { 
    if (resource != null && args != null && args.Length > 0) { 
     return string.Format(resource, args); 
    } else { 
     return resource; 
    } 
} 
3

Ich habe eine fließende Validierung verwendet, um dies zu erreichen. Es spart mir viel Zeit. So sieht mein globalisierter Validator aus. Es bedeutet, dass Sie keine Datenanmerkungen verwenden, aber manchmal werden Datenanmeldungen ein bisschen groß und unordentlich. Hier

ein Beispiel:

(errors.required, Labels.Email und Errors.AlreadyRegistered sind in meinem blobal Ressourcen Ordner.)

public class CreateEmployerValidator : AbstractValidator<CreateEmployerModel> { 
    public RegisterUserValidator() { 
     RuleFor(m => m.Email) 
      .NotEmpty() 
      .WithMessage(String.Format(Errors.Required, new object[] { Labels.Email })) 
      .EmailAddress() 
      .WithMessage(String.Format(Errors.Invalid, new object[] { Labels.Email })) 
      .Must(this.BeUniqueEmail) 
      .WithMessage(String.Format(Errors.AlreadyRegistered, new object[] { Labels.Email })); 
    } 

    public bool BeUniqueEmail(this IValidator validator, string email) { 
     //Database request to check if email already there? 
     ... 
    }  
} 

Wie ich schon sagte, ist es ein Schritt Formulardaten Anmerkungen weg, nur weil ich zu viele Anmerkungen auf meine Methoden bereits schon!

2

Ich füge meine Ergebnisse hinzu, da ich damit kämpfen musste. Vielleicht wird es jemandem helfen.

Wenn Sie von RequiredAttribute ableiten, scheint es die clientseitige Validierung zu unterbrechen. Um dies zu beheben, implementierte ich IClientValidatable und implementierte die GetClientValidationRules-Methode. Resources.GetResources ist eine statische Hilfsmethode, die HttpContext.GetGlobalResourceObject umschließt.

Das benutzerdefinierte Attribut required:

public class LocalizedRequiredAttribute : RequiredAttribute, IClientValidatable 
{ 
    public LocalizedRequiredAttribute(string resourceName) 
    { 
     this.ErrorMessage = Resources.GetResource(resourceName); 
    } 

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     yield return new ModelClientValidationRule 
     { 
      ErrorMessage = this.ErrorMessage, 
      ValidationType= "required" 
     }; 
    } 
} 

Verbrauch:

[LocalizedRequired("SomeResourceName")] 
public string SomeProperty { get; set; } 

und Helfer meine Ressourcen, wenn jemand interessiert ist:

public class Resources 
{ 
    public static string GetResource(string resourceName) 
    { 
     string text = resourceName; 
     if (System.Web.HttpContext.Current != null) 
     { 
      var context = new HttpContextWrapper(System.Web.HttpContext.Current); 
      var globalResourceObject = context.GetGlobalResourceObject(null, resourceName); 
      if (globalResourceObject != null) 
       text = globalResourceObject.ToString(); 
     } 

     return text; 
    } 
} 
Verwandte Themen