2012-07-04 12 views
6

To jest bardzo podobny problem do jednego już pisał tutaj: ASP.NET MVC: Validation messages set in TryUpdateModel not showning ValidationSummaryMVC ValidationSummary ignoruje błędy sprawdzania poprawności poziomie modelu po związaniu z użyciem TryUpdateModel

Nie jestem pewien, czy ten stary wątek był w nawiązaniu do wcześniejszej wersji MVC ale w MVC3 doświadczam dziwnych zachowań wzdłuż podobnych linii.

Mam klasy modelu o nazwie Trade. To dziedziczy po obiekcie IValidatableObject, dlatego implementuje metodę sprawdzania poprawności. W ramach tego mamy pewną walidację modelu jako całości (w przeciwieństwie do adnotacji danych wymuszających weryfikację właściwości). Walidacja jest następująca:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
    var validationResults = new List<ValidationResult>(); 

    if (this.EndDate < this.StartDate) 
    { 
     validationResults.Add(new ValidationResult("End date must be greater than start date")); 
    } 

    return validationResults; 
    } 

Mamy model widoku, aby pomóc w wyświetlaniu transakcji. Zawiera odniesienie do modelu handlowego za pośrednictwem właściwości TradeModel. Zasadniczo model widoku jest modelem handlu, a także dodatkowymi informacjami dla populacji list rozwijanych, takich jak kontrahenci, itp.

Nasza klasa CSHTML zawiera ValidationSummary, z "prawdziwą" jako argumentem, co oznacza, że pokażą tylko błędy modelu.

Jeśli wdrożyć mój HttpPost metody kontrolera dla tworzenia nowego Handluj następująco ...

[HttpPost] 
    public ActionResult Create(FormCollection collection) 
    { 
    var trade = new Trade(); 

    if (this.TryUpdateModel(trade)) 
    { 
     if (this.SaveChanges(this.ModelState, trade)) 
     { 
      return this.RedirectToAction("Index"); 
     } 
    } 

    return this.View(trade); 
    } 

... Kiedy wprowadzić handel z StartDate> DataZakończenia, jestem stwierdzenia, że ​​TryUpdateModel zwraca false i użytkownik jest przekierowywany z powrotem do swojego handlu. Wydaje się to logiczne. Niestety ValidationSummary nie wyświetla żadnych komunikatów o błędach.

Jeśli umieściłem punkt przerwania w metodzie Create i zbadam modelStateState, widzę komunikat o błędzie w słowniku. Jest to sprzeczne z kluczem "TradeModel", a nie z żadną z właściwości. Ponownie wydaje się to logiczne.

Jedna z teorii wyjaśnia, dlaczego tak jest, jest taka, że ​​ValidationSummary zakłada, że ​​wszelkie błędy sprawdzania poprawności w odniesieniu do klucza, który nie jest łańcuchem String.Empty, muszą być błędami sprawdzania poprawności właściwości, ignoruje nasze błędy sprawdzania poprawności, ponieważ mamy model widoku zawierający odniesienie do modelu, w wyniku czego kluczem jest "TradeModel".

Co wieje tę teorię z wody jest taka: jeśli ja przepisać Tworzenie funkcji sterownika następująco ...

[HttpPost] 
    public ActionResult Create(Trade trade, FormCollection collection) 
    { 
    if (this.SaveChanges(this.ModelState, trade)) 
    { 
     return this.RedirectToAction("Index"); 
    } 

    return this.View(trade); 
    } 

... a zatem polegać na MVC wykonywania wiązania „automatycznie”, a Ponownie uruchom ten sam scenariusz testowy, użytkownikowi zostanie wyświetlony komunikat o błędzie, który powinien zostać wyświetlony!

Jeśli dodaję punkt przerwania i zobaczę ModelState, widzę identyczne komunikaty o błędach z tymi samymi kluczami co poprzednio, ale tym razem ValidationSummary je podnosi!

Jeśli mogę zmienić walidacji następująco następnie współpracuje z Tworzenie funkcji sterownika napisany w obu kierunkach:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
    var validationResults = new List<ValidationResult>(); 

    if (this.EndDate < this.StartDate) 
    { 
     validationResults.Add(new ValidationResult("End date must be greater than start date", new[] { "StartDate" })); 
    } 

    return validationResults; 
    } 

więc wyraźnie, że to tylko problem z błędami walidacji poziomie modelu.

Każda pomoc w tej sprawie byłaby bardzo ceniona! Istnieją powody (do których teraz nie wejdę), dlaczego musimy ręcznie utworzyć wystąpienie naszego modelu widoku i wywołać powiązanie za pomocą TryUpdateModel.

Z góry dziękuję!

UPDATE

Wydaje się, że teoria ValidationSummary wyświetlając tylko błędy z kluczem w ModelState z String.Empty kiedy powiedziano, aby wykluczyć błędy nieruchomość jest faktycznie prawdą. Sprawdziłem kod źródłowy i faktycznie używa ViewData.TemplateInfo.HtmlFieldPrefix, aby znaleźć błędy sprawdzania poprawności na poziomie modelu. Domyślnie jest to String.Empty.

Zmiana tej wartości na "TradeModel" wydaje się więc logiczna, ale powoduje, że każdy identyfikator lub nazwa HTML ma być prefiksem, więc powiązanie następnie kończy się niepowodzeniem!

Jeśli model widoku zawiera odniesienie do modelu biznesowego, wszystkie błędy dodane do ModelState przez obiekt IValidatableObject są dodawane z kluczem zawierającym przedrostek równy nazwie właściwości modelu biznesowego w modelu widoku (w naszym przypadku "TradeModel"), w wyniku czego klucze takie jak "TradeModel.CounterpartyId" itp. Błędy na poziomie modelu są dodawane z kluczem równym nazwie właściwości modelu biznesowego modelu widoku ("TradeModel").

Wygląda więc na to, że firma nie może dodać błędów sprawdzania poprawności na poziomie modelu, jeśli model widoku jest skonstruowany w ten sposób.

To, co mnie zastanawia, to dlaczego tak naprawdę działa w naszym "rzeczywistym" projekcie, kiedy funkcja tworzenia kontrolera jest napisana tak, że przyjmuje obiekt modelu widoku Trade jako argument. Pewnie przegapiłem to wczoraj, ale patrząc na to dzisiaj, kiedy MVC się wiąże i wywoływana jest walidacja, wydaje się, że dodaje dodatkowy klucz na końcu słownika o wartości String.Empty. Zawiera duplikat błędów dodanych za pomocą klucza TradeModel. Jak można się spodziewać, ValidationSummary następnie je podnosi!

Dlaczego więc MVC robi to w naszym projekcie na żywo, ale nie w prostej aplikacji testowej?

Widziałem sprawdzanie poprawności uruchamiane dwukrotnie, gdy funkcja kontrolera jest zapisana, aby wziąć model widoku jako argument. Może za każdym razem robi coś subtelnie innego?

UPDATE ... znowu

Powodem działa w naszym realnym projektem jest tam jakiś kod pochowany w naszej sterownika bazowego (z których wszystkie inne dziedziczą), które kopiuje wszystkie znalezione błędy w stanie do modelu nowy wpis z kluczem String.Empty. Ten kod był wywoływany tylko w jednym z dwóch scenariuszy. Tak więc nie ma rzeczywistej różnicy między aplikacjami rzeczywistymi i testowymi.

Teraz rozumiem, co się dzieje i dlaczego ValidationSummary zachowuje się w ten sposób.

+0

Próbowałem ponownie zgłosić problem bez powodzenia. Dla mnie w nowym projekcie MVC3 twoja ** druga akcja ** 'public ActionResult Create (Trade trade, kolekcja FormCollection)' również ** nie wyświetla ** komunikatu potwierdzającego zgodnie z oczekiwaniami. Czy możesz stworzyć repro siebie w pustym projekcie, aby zweryfikować zachowanie, a może gdzieś przesłać? – nemesv

+0

Interesujące. Dziękuję za próbę odtworzenia tego. Stworzę prosty przykład w nowym projekcie, jak sugerujesz. –

+0

Stworzyłem prosty projekt i, jak powiedziałeś, nie działa on z żadną wersją funkcji tworzenia kontrolera. –

Odpowiedz

5

Ok. Jestem teraz w punkcie, w którym sam mogę odpowiedzieć.

Jeśli ValidationSummary jest używany z argumentem ExcludePropertyErrors ustawionym na True, będzie szukał błędów w stanie modelu przy użyciu klucza równego ViewData.TemplateInfo.HtmlFieldPrefix. Domyślnie jest to String.Empty.

Jeśli masz widok modelu, który eksponuje swój model biznesowy, coś takiego:

namespace ValidationSummary.Models 
{ 
    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel.DataAnnotations; 

    public class TradeModel : IValidatableObject 
    { 
     public DateTime StartDate { get; set; } 

     public DateTime EndDate { get; set; } 

     public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
     { 
     List<ValidationResult> validationResults = new List<ValidationResult>(); 

     if (EndDate < StartDate) 
     { 
      validationResults.Add(new ValidationResult("End date must not be before start date")); 
     } 

     return validationResults; 
     } 
    } 
} 

namespace ValidationSummary.ViewModels 
{ 
    public class Trade 
    { 
     public Trade() 
     { 
     this.TradeModel = new Models.TradeModel(); 
     } 

     public Models.TradeModel TradeModel { get; private set; } 
    } 
} 

Kiedy walidacja odbywa, które są dodawane w modelu poziomu (gdzie nie ma błędów (ValidationResults) kolejny argument konstruktora ValidationResult, który przyjmuje nazwy właściwości, są dodawane do ModelState z prefiksem nazwy właściwości modelu widoku - w tym przykładzie "TradeModel".

Jest na to kilka sposobów, nad którymi obecnie się zastanawiamy.

  1. Nie należy wystawiać modelu biznesowego poza modelem widoku. Wiąże się to głównie z wiązaniem z modelem widoku, a nie z modelem biznesowym modelu widoku. Można to zrobić na dwa sposoby: spraw, aby model widoku był podklasą modelu biznesowego; lub zduplikuj właściwości z modelu biznesowego do modelu widoku. Wolę ten pierwszy.
  2. Napisz kod, aby skopiować błędy na poziomie modelu do nowego klucza słownika modelState String.Empty. Niestety nie można tego zrobić elegancko, ponieważ nazwa właściwości modelu widoku, który eksponuje model biznesowy, jest tym, co jest używane jako klucz. Może się różnić w zależności od kontrolera/modelu widoku.
  3. Użyj następujących opcji w widoku. Spowoduje to wyświetlenie komunikatów o błędach, które dotyczą modelu biznesowego. Zasadniczo udawanie, że błędy modelu na poziomie modelu biznesowego są w rzeczywistości błędami własności. Wyświetlacz z nich nie jest taki sam jak dla ValidationSummary, ale może to może być utwardzana z CSS:

    @ Html.ValidationMessageFor (M => m.TradeModel)

  4. podklasa ValidationSummary. Wymagałoby to zmiany go tak, aby wiedział, które klucze w ModelState odnoszą się do właściwości modelu biznesowego modelu widoku.

Powiązane problemy