8

Ich möchte benutzerdefinierte clientseitige Validierer erstellen, aber ich möchte Validierungsregeln über Data Annotations Attribute auf Geschäftslogikebene definieren. Wie kann ich in Laufzeit auf Modellvalidierungsattribute zugreifen?Erhalten Sie Datenanmerkungsattribute aus dem Modell

Ich will 'Generator' schreiben, die diesen Code konvertieren:

public class LoginModel 
{ 
    [Required] 
    [MinLength(3)] 
    public string UserName { get; set; } 

    [Required] 
    public string Password { get; set; } 
} 

in dieser:

var loginViewModel= { 
    UserName: ko.observable().extend({ minLength: 3, required: true }), 
    Password: ko.observable().extend({ required: true }) 
}; 

Aber nicht aus CS- Quellen, natürlich. =)

Vielleicht Reflexion? MSDN:

UPD

ich diese Methode gefunden habe. Aber ich kann nicht verstehen, wie man es benutzt.

+0

Ja, Reflexion. Was sonst? –

+1

Reflektion ist immer eine Option, aber ein bestimmter Grund, warum Sie dies aus Quellen vermeiden möchten? T4 + EnvDTE scheint hier eine gute Wahl zu sein. – decPL

+0

@HenkHolterman Ich habe mvc-Quellen gelesen und diese Methode gefunden: http://msdn.microsoft.com/en-us/library/system.web.mvc.htmlhelper.getunobtrusivevalidationattributes(v=vs.108).aspx aber kann nicht verstehen, wie man es benutzt. Vielleicht hat jemand eine bessere Idee als Nachdenken? =) – letalumil

Antwort

13

Dies ist die universal Art und Weise, wie dies zu tun:

private string GenerateValidationModel<T>() 
{ 
    var name = typeof(T).Name.Replace("Model", "ViewModel"); 
    name = Char.ToLowerInvariant(name[0]) + name.Substring(1); 

    var validationModel = "var " + name + " = {\n"; 

    foreach (var prop in typeof(T).GetProperties()) 
    { 
     object[] attrs = prop.GetCustomAttributes(true); 
     if (attrs == null || attrs.Length == 0) 
      continue; 

     string conds = ""; 

     foreach (Attribute attr in attrs) 
     { 
      if (attr is MinLengthAttribute) 
      { 
       conds += ", minLength: " + (attr as MinLengthAttribute).Length; 
      } 
      else if (attr is RequiredAttribute) 
      { 
       conds += ", required: true"; 
      } 
      // ... 
     } 

     if (conds.Length > 0) 
      validationModel += String.Format("\t{0}: ko.observable().extend({{ {1} }}),\n", prop.Name, conds.Trim(',', ' ')); 
    } 

    return validationModel + "};"; 
} 

Verbrauch:

string validationModel = GenerateValidationModel<LoginModel>(); 

Ausgang:

var loginViewModel = { 
    UserName: ko.observable().extend({ minLength: 3, required: true}), 
    Password: ko.observable().extend({ required: true}), 
}; 

Es ist gute Idee, den Ausgang

zwischenzuspeichern
+0

Genau das, was ich brauche! Ich danke dir sehr! – letalumil

3

Wie oben erwähnt - ich glaube, T4 könnte hier einen Versuch wert sein. Ein großer Vorteil ist, dass es zur Laufzeit nicht ausgeführt wird (obwohl dies möglich ist, wenn dies Ihre Anforderung ist) und Sie alle möglichen Probleme mit der Generierung von Runtime-Dateien vermeiden können. Hoffentlich ein ausreichender Startpunkt:

<#@ template language="C#" debug="True" hostspecific="true" #> 
<#@ output extension="js" #> 
<#@ assembly name="System.Core" #> 
<#@ assembly name="EnvDTE" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#@ import namespace="System.Linq" #> 
<#@ import namespace="EnvDTE" #> 
<# 
    var serviceProvider = Host as IServiceProvider; 
    if (serviceProvider == null) 
    { 
     throw new InvalidOperationException("Host is not IServiceProvider"); 
    } 

    var dte = serviceProvider.GetService(typeof(DTE)) as DTE; 
    if (dte == null) 
    { 
     throw new InvalidOperationException("Unable to resolve DTE"); 
    } 

    var project = dte.Solution.Projects 
           .OfType<Project>() 
           .Single(p => p.Name == "ConsoleApplication2"); 

    var model = project.CodeModel 
         .CodeTypeFromFullName("MyApp.LoginModel") 
        as CodeClass; 
    //might want to have a list/find all items matching some rule 

#> 
var <#= Char.ToLowerInvariant(model.Name[0]) 
     + model.Name.Remove(0, 1).Replace("Model", "ViewModel") #>= { 
<# 
    foreach (var property in model.Members.OfType<CodeProperty>()) 
    { 
     var minLength = property.Attributes 
            .OfType<CodeAttribute>() 
            .FirstOrDefault(a => a.Name == "MinLength"); 
     var required = property.Attributes 
           .OfType<CodeAttribute>() 
           .FirstOrDefault(a => a.Name == "Required"); 

     var koAttributes = new List<String>(); 
     if (minLength != null) 
      koAttributes.Add("minLength: " + minLength.Value); 
     if (required != null) 
      koAttributes.Add("required: true"); 
#> 
    <#= property.Name #>: ko.observable().extend({<#= 
String.Join(", ", koAttributes) #>}), 
<# 
    } 
#> 
} 
+1

Vielen Dank! Ich werde diese Nicht-Runtime-Lösung versuchen :) – letalumil