2015-11-03 12 views
12

Podsumowanie:Jak używać niestandardowych atrybutów walidacji dla modeli potomnych jednostki DB?

chcę walidator adnotacji dane odwołać inną nieruchomość w tej samej klasie (TitleAuthorAndPublishingConfiguration).

Jednak funkcja DB.SaveChanges() nie jest wywoływana bezpośrednio w tej klasie. Raczej jest wywoływany na rodzica tej klasy (WebsiteConfiguration).

Dlatego validationContext.ObjectType powraca WebsiteConfiguration i nie mogę odwoływać się do właściwości TitleAuthorAndPublishingConfiguration w walidatorze adnotacji danych.


WebsiteConfiguration.cs

public class WebsiteConfiguration 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int ID { get; set; } 

    public TitleAuthorAndPublishingConfiguration TitleAuthorAndPublishing { get; set; } 

    public BookChaptersAndSectionsConfiguration BookChaptersAndSections { get; set; } 

    public SocialMediaLoginsConfiguration SocialMediaLogins { get; set; } 

    public TagGroupsConfiguration TagGroups { get; set; } 
} 

public class TitleAuthorAndPublishingConfiguration 
{ 
    public string BookTitle { get; set; } 

    public bool IsPublished { get; set; } 

    // how do I access a property of current model when calling DB.SaveChanges() on parent? 
    [RequiredIfOtherFieldIsEnabled("IsPublished")] 
    public string Publisher { get; set; } 
} 

// ... and other sub models... 

ApplicationDbContext.cs

DbSet<WebsiteConfiguration> WebsiteConfiguration {get;set;} 

Przykład kodu Aktualizacja

public void SeedWebsiteConfiguration() 
    { 
     var titleAuthorAndPublishingConfiguration = new TitleAuthorAndPublishingConfiguration() 
     { 
      // seed values 
     }; 
     var bookChaptersAndSectionsConfiguration = new BookChaptersAndSectionsConfiguration() 
     { 
      // seed values 
     }; 
     var socialMediaLoginConfiguration = new SocialMediaLoginsConfiguration() 
     { 
      // seed values 
     }; 
     var tagGroupsConfiguration = new TagGroupsConfiguration() 
     { 
      // seed values 
     }; 
     var websiteConfiguration = new WebsiteConfiguration() 
     { 
      TitleAuthorAndPublishing = titleAuthorAndPublishingConfiguration, 
      BookChaptersAndSections = bookChaptersAndSectionsConfiguration, 
      SocialMediaLogins = socialMediaLoginConfiguration, 
      TagGroups = tagGroupsConfiguration 
     }; 
     DB.WebsiteConfiguration.Add(websiteConfiguration); 
     DB.SaveChanges(); 
    } 

Code Validator

public class RequiredIfOtherFieldIsEnabledAttribute : ValidationAttribute 
{ 
    private string _ifWhatIsEnabled { get; set; } 


    public RequiredIfOtherFieldIsEnabledAttribute(string IfWhatIsEnabled) 
    { 
     _ifWhatIsEnabled = IfWhatIsEnabled; 
    } 

    protected override ValidationResult IsValid(object currentPropertyValue, ValidationContext validationContext) 
    { 
     var isEnabledProperty = validationContext.ObjectType.GetProperty(_ifWhatIsEnabled); 
     if (isEnabledProperty == null) 
     { 
      return new ValidationResult(
       string.Format("Unknown property: {0}", _ifWhatIsEnabled) 
      ); 
     } 
     var isEnabledPropertyValue = (bool)isEnabledProperty.GetValue(validationContext.ObjectInstance, null); 

     if (isEnabledPropertyValue == true) 
     { 
      if (String.IsNullOrEmpty(currentPropertyValue.ToString())) 
      { 
       return new ValidationResult(String.Format("This field is required if {0} is enabled", isEnabledProperty)); 
      } 
     } 
     return ValidationResult.Success; 
    } 
} 

Pytania

  1. Czy istnieje sposób dla mnie, aby uzyskać dostęp do właściwości modelowych dziecko od validationContext?

  2. Czy jestem błędny w moim podejściu? Czy istnieje lepszy sposób przechowywania wielu modeli jako część większego modelu w jednej tabeli DB?

Miałem nadzieję, że nie będzie wielu tabel konfiguracyjnych i wywołań do DB. (W tym przykładzie są 4 modele potomne, ale w następnej aplikacji może być 10+).

Powyższe ustawienia spełniają moje potrzeby na tak wiele sposobów. Ale nie chcę rezygnować z funkcjonalności DataAnnotations na modelach podrzędnych!


Pytanie Bonus

mam natknąć kilka stanowisk jak ten: How can I tell the Data Annotations validator to also validate complex child properties?

Ale to jest 4 lat, i zastanawiam się, czy coś się zmieniło od tamtego czasu .

Czy próbuję zrobić coś, co jest zasadniczo niemożliwe (lub co najmniej bardzo trudne)?

+0

'Jak mogę uzyskać dostęp do właściwości obecnego modelu Dzwoniąc DB.SaveChanges() na rodzica?' Jestem stara się zrozumieć, co znaczy ... jesteś chcąc uzyskać dostęp do członkowie klasy Wydawcy są wymagane? Czy chcesz to zrobić, używając adnotacji do danych? Jestem zdezorientowany i chciałbym to zrozumieć. –

+0

Chcę walidatora adnotacji danych na właściwość 'TitleAuthorAndPublishingConfiguration', aby odwoływać się do innej właściwości' TitleAuthorAndPublishingConfiguration'. Ale 'DB.SaveChanges()' nie jest wywoływana bezpośrednio na tej klasie, a raczej na jej rodzica - 'WebsiteConfiguration'. –

+0

Mam zaktualizowane pytanie, mam nadzieję, że będzie jaśniejsze –

Odpowiedz

2

Am I starają się robić coś, co jest w zasadzie niemożliwe (albo przynajmniej bardzo trudne)?

Nie, istnieje bardzo proste rozwiązanie, które idealnie integruje się z ramami i technologiami wykorzystującymi DataAnnotations.

Można utworzyć niestandardową ValidationAttribute, która jest wywoływana przez EF Validation i wywoływać w środku Validator.TryValidateObject. W ten sposób, gdy wywoływana jest funkcja CustomValidation.IsValid, uruchamiana jest ręczna weryfikacja obiektu złożonego podrzędnego i tak dalej dla całego wykresu obiektów. Jako bonus możesz zebrać wszystkie błędy dzięki CompositeValidationResult.

tj

using System; 
using System.ComponentModel.DataAnnotations; 
using System.Collections.Generic; 

public class Program 
{ 
    public static void Main() { 
    var person = new Person { 
     Address = new Address { 
     City = "SmallVille", 
     State = "TX", 
     Zip = new ZipCode() 
     }, 
     Name = "Kent" 
    }; 

    var context = new ValidationContext(person, null, null); 
    var results = new List<ValidationResult>(); 

    Validator.TryValidateObject(person, context, results, true); 

    PrintResults(results, 0); 

    Console.ReadKey(); 
} 

private static void PrintResults(IEnumerable<ValidationResult> results, Int32 indentationLevel) { 
    foreach (var validationResult in results) { 
     Console.WriteLine(validationResult.ErrorMessage); 
     Console.WriteLine(); 

     if (validationResult is CompositeValidationResult) { 
     PrintResults(((CompositeValidationResult)validationResult).Results, indentationLevel + 1); 
     } 
    } 
} 

} 

public class ValidateObjectAttribute: ValidationAttribute { 
    protected override ValidationResult IsValid(object value, ValidationContext validationContext) { 
     var results = new List<ValidationResult>(); 
     var context = new ValidationContext(value, null, null); 

     Validator.TryValidateObject(value, context, results, true); 

     if (results.Count != 0) { 
     var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName)); 
     results.ForEach(compositeResults.AddResult); 

     return compositeResults; 
     } 

     return ValidationResult.Success; 
    } 
} 

public class CompositeValidationResult: ValidationResult { 
    private readonly List<ValidationResult> _results = new List<ValidationResult>(); 

    public IEnumerable<ValidationResult> Results { 
     get { 
     return _results; 
     } 
    } 

    public CompositeValidationResult(string errorMessage) : base(errorMessage) {} 
    public CompositeValidationResult(string errorMessage, IEnumerable<string> memberNames) : base(errorMessage, memberNames) {} 
    protected CompositeValidationResult(ValidationResult validationResult) : base(validationResult) {} 

    public void AddResult(ValidationResult validationResult) { 
     _results.Add(validationResult); 
    } 
} 

public class Person { 
    [Required] 
    public String Name { get; set; } 

    [Required, ValidateObject] 
    public Address Address { get; set; } 
} 

public class Address { 
    [Required] 
    public String Street1 { get; set; } 

    public String Street2 { get; set; } 

    [Required] 
    public String City { get; set; } 

    [Required] 
    public String State { get; set; } 

    [Required, ValidateObject] 
    public ZipCode Zip { get; set; } 
} 

public class ZipCode { 
    [Required] 
    public String PrimaryCode { get; set; } 

    public String SubCode { get; set; } 
} 
+0

Dziękuję. Dam ci to. –

Powiązane problemy