13

Oto scenariusz:Wskazówki dotyczące POCO Validation z ASP.NET MVC/Entity Framework

  • ASP.NET Web Application MVC2
  • Entity Framework 4 (Czysta poco jest kontekst danych klienta)
  • Repository Wzór
  • Jednostka tempo prac
  • Dependency Injection
  • usługi Warstwa pośrednicząca Controller -> Repository

W zasadzie wszystkie fajne rzeczy. :)

Przepływ zdarzeń dla podstawowych operacji UI ("Dodawanie post"):

  1. kontroler wzywa Dodaj (post) metodę na warstwie usług
  2. warstwa Usługa nazywa Dodaj (T) na repozytorium
  3. Repository połączeń AddObject (T) na zamówienie kontekstu danych
  4. Controller wywołuje Comm to() na jednostce pracy

Teraz próbuję ustalić, gdzie mogę umieścić moją walidację.

Na tym etapie muszę dwa rodzaje walidacji

  1. Prosty, niezależna walidacja POCO takie jak „post musi mieć tytuł”. Wydaje się to naturalne dopasowanie do Data Annotations na POCO.
  2. Kompleksowa walidacja biznesowa, np. "Nie można dodać komentarza do zablokowanego wpisu". Nie można tego zrobić za pomocą adnotacji danych.

Teraz Czytam „Programowanie Entity Framework, drugie wydanie” Julie Lerman (co jest doskonałe BTW) i zostały patrząc do podpinania do zdarzenia SavingChanges w celu przeprowadzenia „last minute” walidacja . Byłby to dobry sposób na zapewnienie sprawdzania poprawności: zawsze dzieje się za każdym razem, gdy robię "coś" (dodawanie, modyfikowanie, usuwanie), ale jest to również trochę spóźnione IMO (ponieważ elementy są już w menedżerze stanu) - więc co mogę zrobić zrobić, jeśli sprawdzanie poprawności nie powiedzie się, usunąć je?

Mogę oczywiście uczynić z mojego urządzenia POCO interfejs (powiedz "IValidatable") i wywołać metodę w tym interfejsie podczas tego wydarzenia.

Ale wydaje się, że "za późno" na walidację biznesu - czy to jest konsensus?

Po prostu szukam wskazówek tutaj, próbuję zaprojektować wielokrotnego użytku, inteligentny schemat sprawdzania poprawności złożonej logiki biznesowej, biorąc pod uwagę powyższą architekturę.

Innym krzywa-ball dla Ciebie - jak wiadomo, POCO na EF oznaczać POCO mają wszystkie właściwości na DB - więc może mieć „postID” własności, z get/set dostępowych (jako potrzeb EF aby uzyskać/ustawić te właściwości).

Ale problem polega na tym, że "PostID" to kolumna o nazwie , czyli jak chronić pole przed ustawieniem explicity? Np. Jeśli (z jakiegoś powodu) wykonaj następujące czynności:

var post = service.FindSingle(10); 
post.PostId = 10; 
unitOfWork.Commit(); 

Spowoduje to zgłoszenie wyjątku SqlException. Jak mogę temu zapobiec? Nie mogę "ukryć" właściwości (uczynić ją prywatną, a nawet wewnętrzną), ponieważ POCO są w osobnym zestawie do repozytorium.

Uwaga dotycząca sprawdzania poprawności - planuję utworzyć niestandardowe wyjątki (wynikające z wyjątku). Więc gdy sprawdzanie poprawności nie powiedzie się, muszę rzucić te wyjątki.

ten sposób mogę kodować coś takiego na moim kontrolera:

[HttpPost] 
public ActionResult AddPost(Post post) 
{ 
    try 
    { 
     IUnitOfWork uow = new UnitOfWork(); 
     postService.Add(post); 
     uow.Commit(); 
    } 
    catch(InvalidPostOperation ipo) 
    { 
     // add error to viewmodel 
    } 
} 

będę musiał ręcznie zrobić walidacji na każdym razem warstwy usług i czy dodatek? Jak mogę sobie z tym poradzić? (jak to jest w jednostce pracy, a nie w warstwie usług).

Tak, aby temu zapobiec, że jest „wszędzie” kwestia, oto moje pytania:

  1. Prosta walidacja POCO - Należy to zrobić z danymi adnotacje? Plusy/minusy/Gotcha?
  2. W jakich okolicznościach (jeśli w ogóle) powinniśmy być podłączeni do zdarzenia zdarzenia Kontekstu danych EF w celu potwierdzenia?
  3. Gdzie powinienem wykonywać kompleksową walidację biznesową? W usłudze explicity lub metoda na POCO (którą mogę wywołać z usługi). Jak mogę stworzyć schemat inteligentnego/wielokrotnego użytku?
  4. Jak możemy "ukryć" automatycznie generowane właściwości POCO przed manipulowaniem?

Wszelkie myśli będą najbardziej cenione.

Przepraszam, jeśli ten wpis jest "zbyt długi", ale jest to ważny problem i można go rozwiązać na wiele sposobów, dlatego chciałem podać wszystkie informacje, aby uzyskać najlepszą możliwą odpowiedź.

Dzięki.

EDIT

Poniższa odpowiedź jest pomocne, ale nadal jestem (idealnie) szuka więcej myśli. Ktoś jeszcze?

+0

Zbyt wiele pytań w jednym, ale jeśli chodzi o # 3, należy pamiętać, że sprawdzanie poprawności ma znaczenie kontekstowe (lub oparte na zadaniach). To kuszące, aby utworzyć własność 'IsValid' na twoim' Post', ale gdzieś po linii będziesz potrzebował 'IsValidForDraft', który jest używany do zapisywania wersji roboczych, które nie posiadają pewnych informacji. I na pewno POCO nie powinien być świadom pojęcia "projektu publikacji". :) – bzlm

+1

Aw, daj spokój, dlaczego głosować, aby zamknąć? Rozumiem, że jest to "długie" pytanie. Ale właśnie dlatego mam 4 pytania na końcu. To, że pytam o 4 rzeczy, nie oznacza, że ​​ten Q zasługuje na głos, by zamknąć to, że jest "zbyt ogólnikowy". Wszystkie 4 q odnoszą się do jednego tematu. – RPM1984

+0

@bzlm - tak, rozumiesz. :) – RPM1984

Odpowiedz

1
  1. Cóż, tak jak powiedziałeś, DataAnnotations nie jest odpowiedni we wszystkich sytuacjach. W moim doświadczeniu są to głównie skomplikowane sprawdzanie poprawności (wiele właściwości i wiele właściwości różnych obiektów).
  2. Gdybym był tobą, zostawiłbym sprawdzanie poprawności biznesowej/domeny w warstwie danych (EF) w jak największym stopniu. Jeśli istnieje scenariusz sprawdzania poprawności warstwy danych, to dobrze (np. Sprawdzanie złożonych relacji rodzic/dziecko - jest to czysto DB).
  3. Tak, złożona walidacja biznesowa powinna znajdować się w warstwie usługi lub w obiektach modelu (dołączone, za pośrednictwem klas częściowych lub niektórych metod dziedziczenia: interfejsy/klasy pochodne). Dyskutuje się o tym między ludźmi z ActiveRecord, ludźmi z Repository Pattern i osobami z DDD, ale z tym, co działa dla ciebie, jest prosty i umożliwi szybkie wdrożenie i niską cenę utrzymania aplikacji. Jest to prosta wersja example of how you might attach more complex validation to domain objects, ale nadal jest kompatybilna z interfejsem DataAnnotations i dzięki temu jest przyjazna dla MVC.
  4. Dobre pytanie. - Jeszcze nie znalazłem rozwiązania, z którego jestem w 100% zadowolony. Grałem z pomysłami seterów prywatnych i nie jest to wspaniałe. Zapoznaj się szybko z tym summarized Evans DDD book. Jest to świetny szybki odczyt i może dostarczyć wgląd w cel i różnicę między obiektami modelu a obiektami wartości. W tym miejscu myślę, że projektowanie obiektów złagodzi problemy związane z posiadaniem "manipulacji" nieruchomościami (jak to nazywacie), ale bez naprawy widoczności nieruchomości. To znaczy, inne rozwiązanie może leżeć gdzie indziej. Mam nadzieję że to pomoże.
+0

Dzięki, pobieram i oglądam tę książkę. Jestem dość zadowolony z podstawowych adnotacji danych na temat POCO, bardziej zainteresowanych złożoną weryfikacją. Ten przykład jest bardzo interesujący (i sprytny) weźmie to pod uwagę. Z 4) nie może uczynić go prywatnym.Jak już wspomniałem, POCO są w jednym zestawie (bez EF ref), a EF/repo jest w innym. Więc musi być publicznie. Pomyślałem, że istnieje sposób, aby powiedzieć "seter tylko dostępny dla określonego zespołu". Chodzi o to, chcę tylko, aby repozytorium miało "ustawiony" dostęp do właściwości PostId. – RPM1984

+0

Ten modyfikator widoczności, o którym myślisz, to 'Internal' i dotyczy tylko klas (98%?). Nie właściwości. Przepraszam, że nie mogłem pomóc więcej z # 4, dlatego zasugerowałem tę książkę. Mam te same obawy. Jedną rzeczą, która może pomóc w tym, jest to? Czy uważasz, że nie masz go w osobnym zespole? Miałem wszystkie swoje rozwiązania z oddzielnymi danymi, usługami, testami i projektami MVC/Web, dopóki nie uznałem, że to zbyt dużo kosztów utrzymania. Teraz mam dane/modele/usługi wszystkie w jednym projekcie/złożeniu. Łatwiejsze dla mnie i wciąż mogę uzyskać tę samą logiczną separację, jakiej potrzebuję. Spróbuj. –

+0

Mogę się mylić, [możesz ustawić członka na "Wewnętrzny"] (http://msdn.microsoft.com/en-us/library/7c5ka91b.aspx) –

1

Hej, chyba trochę późno, ale tu idzie tak ...

Wszystko zależy od architektury, tj Czy istnieje logiczny separacja, w aplikacji: UI, Layer Service, Repository warstw. Jeśli podłączysz się do zdarzenia Save, jak dokładnie to się stanie? Z tego co zauważyłem, będziesz wywoływał tylko repozytorium Warstwa dla Perspektywy? Jednak podpinacie się na zdarzenie save, przekazując kontrolę do warstwy Service Layer/Business Layer, co wtedy zmusza do oszczędzania przez prawo?

Osobiście uważam, że warstwa usługi/warstwa biznesowa powinna zająć się jej ukończeniem, a następnie powiedzieć: hej mr repo layer -> zapisz ten obiekt.

Jeśli chodzi o sprawdzanie poprawności, w interfejsie użytkownika należy używać adnotacji danych, więc prosta odwzorowanie, takie jak [Wymagane] itd., Będzie pomocne przy walidacji po stronie klienta, ale złożona logika biznesowa lub złożona weryfikacja powinny zostać włączone do warstwy usługi/business layer, w ten sposób można go ponownie wykorzystać we wszystkich obiektach/obiektach/POCOS itp.

Jeśli chodzi o zapobieganie manipulowaniu niektórymi prywatnymi polami ... zezwala się jedynie na to, aby warstwa usług/warstwa biznesowa faktycznie ustawiała obiekt, który będzie trwało (tak mam na myśli :) ...) ręcznie koduję to, czułem, że to była najbezpieczniejsza opcja dla mnie tak czy inaczej, tak jak to zrobię:

var updatedpost = _repo.GetPost(post.postid); 
updatedpost.comment = post.comment; 
updatedpost.timestamp = datetime.now; 

Trochę marnotrawstwa, ale w ten sposób twoja warstwa buseinss przejmuje kontrolę, jednak to jest po prostu moje doświadczenie, może jestem w błędzie, dużo czytałem na temat wiązania modelu, validaitonu i innych rzeczy, ale wydawało się, że są przypadki, w których rzeczy nigdy nie działają tak, oczekiwane np [Wymagany] atrybut (patrz: Brad WIlson) post.

Powiązane problemy