2013-07-23 20 views
7

Mam aplikację, która używa wzorca MVVM i chciałbym wdrożyć sprawdzanie poprawności, gdy użytkownik jest wypełnienie informacji.jest to dobry pomysł, że model widoku implementuje IDataErrorInfo do sprawdzania poprawności?

Chciałbym użyć IDataErrorInfo, ale nie wiem, czy to dobry pomysł, że mój model widoku implementuje ten interfejs, czy lepiej jest utworzyć nową klasę. Jaki jest najlepszy sposób sprawdzenia poprawności implikacji za pomocą IDataErrorInfo i wzorca MVVM?

EDYCJA: Widzę, że w niektórych przykładach implementacja jest w modelu (to nie jest to samo, co model widoku), ale w moim przypadku model jest w zasadzie jednostkami POCO, które utworzę z mojej bazy danych podczas tworzenia mój model edmx z strukturą encji, więc chciałbym uniknąć konieczności modyfikacji tych encji, ponieważ jeśli nie potrzebuję aktualizacji mojego modelu, musiałbym ponownie wykonać pracę.

Dzięki.

+4

dobre pytanie. +1 –

+3

Powiedziałbym, że to bardzo dobry pomysł, aby VM zaimplementowała 'IDataErrorInfo'. Ten interfejs w znacznym stopniu "pomaga" w przekazywaniu informacji do użytkownika (widok) w poszukiwaniu błędów w tle (błędy w danych). to jest dokładnie to, do czego służy VM i byłoby całkowicie ważne, aby ją tam było. [Komentarze w tej odpowiedzi] (http://stackoverflow.com/a/341832/1834662) i jeden poniżej kłóci się z posiadaniem go w Modelu. Zdefiniowanie go w innej klasie jest niż Modelem, a VM nie byłoby "złe" dla abstrakcji, ale może nie warto. – Viv

+1

..cont'd. Jeśli to, co sprawdzasz za pomocą 'IDataErrorInfo' jest tak skomplikowaną logiką, że chciałbyś go ponownie użyć, to dosłownie przeniesie te kontrole funkcjonalne do usługi, a następnie zmusiłoby ją do wdrożenia interfejsu i do VM korzysta z usługi, aby uzyskać dostęp do skomplikowanej logiki walidacji. W ten sposób uzyskasz ponowne użycie kodu i udostępnianie, a także pozostaniesz czysty i prosty z każdą maszyną wirtualną, która sama implementuje 'IDataErrorInfo'. – Viv

Odpowiedz

2

To jest zawsze dobry pomysł, aby oddzielić logikę walidacji z UI. W ten sposób użycie IDataErrorInfo jest poprawne.

Między modelem i modelem widoku preferuję implementację IDataErrorInfo w modelu widoku, ponieważ interfejs ten jest używany przez interfejs użytkownika. Możesz symulować interfejs użytkownika poprzez wywoływanie indeksatorów bezpośrednio w kodzie testowym, ale jeśli naprawdę potrzebujesz logiki sprawdzania poprawności w warstwie logiki biznesowej, takie wywołanie nie ma większego sensu.

W naszym projekcie walidacja jest bardziej niezależnym komponentem, który może być używany zarówno przez warstwę prezentacji, jak i warstwę logiki biznesowej według konfiguracji. Z perspektywy modelu widoku jest bardzo cienka, zawiera tylko wywołanie i konstruuje wynik sprawdzania poprawności wewnątrz indeksera.

Inną kwestią jest INotifyDataErrorInfo dostarczana przez .Net 4.5 i Silverlight. Zapewnia ona więcej wyników sprawdzania poprawności z jednej właściwości i asynchroniczną weryfikację dla czasochłonnego sprawdzania poprawności, co jest tym, czego chcemy po aktualizacji do wersji .Net 4.5.

Mam nadzieję, że ci to pomoże.

2

Jeśli masz obiekt lub typ niestandardowy (np. Osoba, student itp.), Który przechowuje dane, to musisz wdrożyć IDataErrorInfo w swoim podmiocie lub typie niestandardowym. Załóżmy, że masz widok, który pozwala wprowadzić dane Ucznia i masz jego ViewModel StudentViewModel i ten ViewModel ma właściwość Student typu Student, którego właściwość (jak Name, Age itp.) Jest powiązana z Controls of View. Dla Walidacje na ogień i zmiany w celu odzwierciedlenia na interfejsie musi wdrożyć IDataErrorInfo w tym Student klasy nie w ViewModel a także trzeba będzie wdrożyć INotifyPropertyChanged w tej klasie. Tak więc rozumiem, jeśli masz kilka właściwości w swoim ViewModelu, które są typu (string i ValueTypes) i są powiązane z View i chcesz zastosować Validations na nich, to musisz Implementować IDataErrorInfo w tobie ViewModel. A jeśli masz CustomType/Entity, musisz wdrożyć interfejs w tych klasach, które nie są w ViewModel.

To nie moje rozumienie to musi wdrożyć IDataErrorInfo i INotifyPropertyChanged w klasie, której EndProperties (jak nazwisko, wiek studenta) są zbindowanych aby zobaczyć steruje jeśli chcesz Walidacje na ogień.

//Interface which has fields of Student class on which ValidationAttribute are to be applied 
public interface IStudent 
{ 
    [Required(ErrorMessage = "Name is required")] 
    string Name { get; set; } 

    [Range(10, 90, ErrorMessage = "Age should be between 10 and 90")] 
    int Age { get; set; } 
} 
//Partial Class to implement IStudent 
public partial class Student : IStudent 
{ 

} 

//POCO 
public partial class Student : INotifyPropertyChanged 
{ 
    private string name; 
    private int age; 

    public string Name 
    { 
     get 
     { 
      return name; 
     } 
     set 
     { 
      name = value; 

      Notify("Name"); 
     } 
    } 

    public int Age 
    { 
     get 
     { 
      return age; 
     } 
     set 
     { 
      age = value; 

      Notify("Age"); 
     } 
    } 
    private void Notify(string propName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propName)); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 
+0

Rozumiem ten pomysł. W jaki więc sposób mogę wdrożyć sprawdzanie poprawności w moich jednostkach POCO? są one generowane przez szablon T4 z mojego modelu edmx, który jest tworzony z mojej bazy danych. Jeśli zaktualizuję bazę danych i będę musiał ponownie utworzyć encje, straciłem wszystkie zmiany (w tym przypadku sprawdzanie poprawności). Czy istnieje sposób implementacji sprawdzania poprawności w jednostkach generowanych z bazy danych? –

+0

Czy używasz sprawdzania poprawności opartego na atrybutach? – ethicallogics

+0

Przez mnoment nie używam żadnego rodzaju sprawdzania poprawności, staram się wybrać najlepszą opcję. –

2

Zgodziłbym się z ogromną większością komentarzy na ten temat, ale odpowiadam na propozycję mojego "uaktualnienia" do tego interfejsu.

Problem, który widzę w interfejsie IDataErrorInfo polega na tym, że odnosi się tylko do jednego błędu na raz. Dlatego dodałem dodatkowe pole w moim BaseDataType klasy (klasy bazowej dla wszystkich moich typów danych):

protected ObservableCollection<string> errors = new ObservableCollection<string>(); 

I dodaje następujące właściwości:

// this just enables me to add into the error collection from outside this class 
public ObservableCollection<string> ExternalErrors 
{ 
    get { return externalErrors; } 
} 

public override ObservableCollection<string> Errors 
{ 
    get 
    { 
     errors = new ObservableCollection<string>(); 
     // add properties to validate 
     errors.AddUniqueIfNotEmpty(this["Property1ToValidate"]); 
     errors.AddUniqueIfNotEmpty(this["Property2ToValidate"]); 
     errors.AddUniqueIfNotEmpty(this["Property3ToValidate"]); 
     // add external errors (from view models) 
     errors.AddRange(ExternalErrors); 
     return errors; 
    } 
} 

public virtual bool HasError 
{ 
    get { return Errors != null && Errors.Count > 0; } 
} 

Sposób AddUniqueIfNotEmpty jest metoda rozszerzenie i jestem pewien, że wszyscy potraficie zgadnąć, co robi.

Dzięki temu mogę powiązać bezpośrednio z kolekcją błędów w widokach, a nawet lepiej, powiązać z właściwością HasError przy użyciu BoolToVisibilityConverter w celu ukrycia kontroli, która wyświetla błędy, gdy kolekcja jest pusta.

1
<TextBox Text="{Binding Path=MyCoolProperty, ValidationOnDataErrors=true}" 

może brakuje czegoś, ale jeśli masz powiązań takich jak ta - klasie z „MyCoolProperty” musiały wdrożyć INotifyPropertyChanges I IDataErrorInfo - inaczej nie będzie działać.

więc powiedziałbym, że pytanie nie brzmi: „powinny wdrożyć IDataErrorInfo”, ale może jak wdrożyć IDataErrorInfo