Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Uwaga / Notatka
Dopiero od EF4.1 — funkcje, interfejsy API itp. omówione na tej stronie zostały wprowadzone w Entity Framework 4.1. Jeśli używasz starszej wersji, niektóre lub wszystkie informacje nie mają zastosowania
Zawartość na tej stronie została zaadaptowana z artykułu pierwotnie napisanego przez Julie Lerman (https://thedatafarm.com).
Program Entity Framework udostępnia wiele różnych funkcji weryfikacji, które mogą być przekazywane do interfejsu użytkownika na potrzeby weryfikacji po stronie klienta lub do sprawdzania poprawności po stronie serwera. W przypadku używania kodu najpierw można określić weryfikacje przy użyciu adnotacji lub płynnych konfiguracji interfejsu API. Dodatkowe, bardziej złożone walidacje można określić w kodzie i będą działać bez względu na to, czy model bazuje na podejściu 'code first', 'model first' czy 'database first'.
Model
Zademonstruję sprawdzanie poprawności przy użyciu prostego przykładu dwóch klas: Blog i Post.
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set; }
public DateTime DateCreated { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public ICollection<Comment> Comments { get; set; }
}
Adnotacje danych
Code First używa adnotacji z zestawu System.ComponentModel.DataAnnotations jako jednego ze sposobów konfigurowania klas Code First. Wśród tych adnotacji są te, które zapewniają reguły, takie jak Required, MaxLength i MinLength. Wiele aplikacji klienckich platformy .NET rozpoznaje również te adnotacje, na przykład ASP.NET MVC. Weryfikację po stronie klienta i po stronie serwera można wykonać przy użyciu tych adnotacji. Możesz na przykład wymusić, że właściwość Tytuł blogu będzie wymaganą właściwością.
[Required]
public string Title { get; set; }
Bez dodatkowych zmian kodu lub znaczników w aplikacji istniejąca aplikacja MVC przeprowadzi walidację po stronie klienta, nawet dynamicznie tworząc komunikat przy użyciu właściwości i nazw adnotacji.
W metodzie odsyłania tego widoku Create, Entity Framework służy do zapisywania nowego bloga w bazie danych, ale weryfikacja po stronie klienta MVC jest wyzwalana, zanim aplikacja dotrze do tego kodu.
Weryfikacja po stronie klienta nie jest jednak niezawodna. Użytkownicy mogą jeszcze wpływać na funkcje przeglądarki lub gorzej, haker może użyć pewnych sztuczek, aby uniknąć weryfikacji interfejsu użytkownika. Jednak program Entity Framework rozpozna również adnotację Required i zweryfikuje ją.
Prostym sposobem testowania jest wyłączenie funkcji weryfikacji po stronie klienta MVC. Można to zrobić w pliku web.config aplikacji MVC. Sekcja appSettings zawiera klucz clientValidationEnabled. Ustawienie tego klucza na wartość false uniemożliwi interfejsowi użytkownika wykonywanie walidacji.
<appSettings>
<add key="ClientValidationEnabled"value="false"/>
...
</appSettings>
Nawet po wyłączeniu weryfikacji po stronie klienta otrzymasz tę samą odpowiedź w aplikacji. Komunikat o błędzie "Pole tytułu jest wymagane" będzie wyświetlany tak jak poprzednio. Z wyjątkiem teraz będzie to wynik weryfikacji po stronie serwera. Entity Framework przeprowadzi walidację adnotacji Required (zanim jeszcze podejmie próbę zbudowania polecenia INSERT do wysłania do bazy danych) i zwróci błąd do MVC, które wyświetli komunikat.
Fluent API
Możesz użyć płynnego interfejsu API Code First zamiast adnotacji, aby uzyskać tę samą weryfikację zarówno po stronie klienta, jak i serwera. Zamiast używać Required, pokażę ci to na przykładzie weryfikacji MaxLength.
Konfiguracje interfejsu Fluent API są stosowane podczas procesu budowania modelu z klas przy użyciu podejścia "code first". Konfiguracje można wprowadzić, przesłaniając metodę OnModelCreating w klasie DbContext. Oto konfiguracja określająca, że właściwość BloggerName nie może być dłuższa niż 10 znaków.
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(p => p.BloggerName).HasMaxLength(10);
}
}
Błędy walidacji zgłaszane na podstawie konfiguracji interfejsu API Fluent nie będą automatycznie docierać do interfejsu użytkownika, ale można przechwycić je w kodzie, a następnie odpowiednio zareagować na nie.
Oto kod błędu obsługi wyjątków w klasie BlogController aplikacji, który przechwytuje ten błąd weryfikacji, gdy program Entity Framework próbuje zapisać blog z wartością BloggerName, która przekracza maksymalną 10 znaków.
[HttpPost]
public ActionResult Edit(int id, Blog blog)
{
try
{
db.Entry(blog).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbEntityValidationException ex)
{
var error = ex.EntityValidationErrors.First().ValidationErrors.First();
this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
return View();
}
}
Walidacja nie jest automatycznie przekazywana z powrotem do widoku, dlatego jest używany dodatkowy kod, którego używa ModelState.AddModelError . Dzięki temu szczegóły błędu trafią do widoku, który następnie użyje narzędzia ValidationMessageFor Htmlhelper do ich wyświetlenia.
@Html.ValidationMessageFor(model => model.BloggerName)
IValidatableObject
IValidatableObject to interfejs, który znajduje się w systemie System.ComponentModel.DataAnnotations. Chociaż nie jest on częścią interfejsu API programu Entity Framework, nadal można go wykorzystać do walidacji po stronie serwera w klasach programu Entity Framework.
IValidatableObject udostępnia metodę, którą Entity Framework wywoła podczas SaveChanges, lub którą możesz wywołać samodzielnie, kiedy tylko chcesz zweryfikować klasy.
Konfiguracje, takie jak Required i MaxLength przeprowadzają walidację w jednym polu. W metodzie Validate można mieć jeszcze bardziej złożoną logikę, na przykład porównując dwa pola.
W poniższym przykładzie klasa Blog została rozszerzona, aby zaimplementować IValidatableObject, a następnie podano regułę, której Title i BloggerName nie mogą być zgodne.
public class Blog : IValidatableObject
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string BloggerName { get; set; }
public DateTime DateCreated { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Title == BloggerName)
{
yield return new ValidationResult(
"Blog Title cannot match Blogger Name",
new[] { nameof(Title), nameof(BloggerName) });
}
}
}
Konstruktor ValidationResult przyjmuje string, który reprezentuje komunikat o błędzie, oraz tablicę elementów string, które reprezentują nazwy członków skojarzonych z walidacją. Ponieważ ta walidacja sprawdza zarówno wartości , Title jak i BloggerName, obie nazwy właściwości są zwracane.
W przeciwieństwie do walidacji dostarczonej przez Fluent API, wynik tej walidacji zostanie rozpoznany przez Widok, a użycie obsługi wyjątków, które wykorzystałem wcześniej do dodania błędu do ModelState, jest niepotrzebne. Ponieważ ustawiam obie nazwy właściwości w ValidationResult, klasa MVC HtmlHelpers wyświetla komunikat o błędzie dla obu tych właściwości.
DbContext.ValidateEntity
DbContext ma metodę zastępowalną o nazwie ValidateEntity. Po wywołaniu SaveChanges, program Entity Framework wywoła tę metodę dla każdej jednostki w swojej pamięci podręcznej, której stan nie jest Unchanged. Logikę walidacji można umieścić bezpośrednio w tym miejscu, a nawet użyć tej metody, aby wywołać na przykład Blog.Validate metodę dodaną w poprzedniej sekcji.
Oto przykład przesłonięcia ValidateEntity, które weryfikuje nowe Post, aby upewnić się, że tytuł wpisu nie został już użyty. Najpierw sprawdza, czy obiekt jest wpisem i czy jego stan to Dodany. Jeśli tak jest, sprawdza w bazie danych, czy istnieje już wpis o tym samym tytule. Jeśli istnieje już istniejący wpis, zostanie utworzony nowy DbEntityValidationResult .
DbEntityValidationResult mieści zarówno DbEntityEntry jak i ICollection<DbValidationErrors> dla pojedynczego podmiotu. Na początku tej metody następuje utworzenie instancji DbEntityValidationResult, a następnie wszelkie wykryte błędy są dodawane do jego zbioru ValidationErrors.
protected override DbEntityValidationResult ValidateEntity (
System.Data.Entity.Infrastructure.DbEntityEntry entityEntry,
IDictionary<object, object> items)
{
var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
if (entityEntry.Entity is Post post && entityEntry.State == EntityState.Added)
{
// Check for uniqueness of post title
if (Posts.Where(p => p.Title == post.Title).Any())
{
result.ValidationErrors.Add(
new System.Data.Entity.Validation.DbValidationError(
nameof(Title),
"Post title must be unique."));
}
}
if (result.ValidationErrors.Count > 0)
{
return result;
}
else
{
return base.ValidateEntity(entityEntry, items);
}
}
Jawne wyzwalanie walidacji
Wywołanie SaveChanges powoduje uruchomienie wszystkich weryfikacji opisanych w tym artykule. Ale nie musisz polegać na SaveChanges. Możesz woleć zweryfikować gdzie indziej w swojej aplikacji.
DbContext.GetValidationErrors wywoła wszystkie walidacje, zdefiniowane przez adnotacje lub Fluent API, walidację utworzoną w IValidatableObject (na przykład Blog.Validate), oraz walidacje wykonywane w metodzie DbContext.ValidateEntity.
Poniższy kod spowoduje wywołanie GetValidationErrors na bieżącym wystąpieniu DbContext.
ValidationErrors są grupowane według typu jednostki na DbEntityValidationResult. Kod iteruje najpierw przez DbEntityValidationResult zwrócone przez metodę, a następnie przez poszczególne DbValidationError w środku.
foreach (var validationResult in db.GetValidationErrors())
{
foreach (var error in validationResult.ValidationErrors)
{
Debug.WriteLine(
"Entity Property: {0}, Error {1}",
error.PropertyName,
error.ErrorMessage);
}
}
Inne zagadnienia dotyczące korzystania z weryfikacji
Poniżej przedstawiono kilka innych kwestii, które należy wziąć pod uwagę podczas korzystania z weryfikacji programu Entity Framework:
- Ładowanie z opóźnieniem jest wyłączone podczas walidacji
- Program EF zweryfikuje adnotacje danych w odniesieniu do właściwości niemapowanych (czyli takich, które nie są przypisane do kolumny w bazie danych)
- Walidacja jest wykonywana po wykryciu zmian podczas
SaveChanges. Jeśli wprowadzasz zmiany podczas walidacji, twoim obowiązkiem jest powiadomienie monitora zmian -
DbUnexpectedValidationExceptionjest zgłaszany, jeśli podczas walidacji wystąpią błędy - Aspekty, które Entity Framework uwzględnia w modelu (maksymalna długość, wymagane itp.) spowodują walidację, nawet jeśli w klasach nie ma adnotacji danych ani nie użyto projektanta EF do utworzenia modelu.
- Reguły pierwszeństwa:
- Wywołania interfejsu API Fluent zastępują odpowiednie adnotacje danych
- Kolejność wykonywania:
- Walidacja właściwości jest wykonywana przed walidacją typu
- Walidacja typu występuje tylko wtedy, gdy walidacja właściwości zakończy się pomyślnie
- Jeśli właściwość jest złożona, jej walidacja będzie również obejmować:
- Walidacja na poziomie właściwości we właściwościach typu złożonego
- Walidacja na poziomie typu złożonego, w tym
IValidatableObjectwalidacja typu złożonego
Podsumowanie
API walidacji w Entity Framework dobrze współpracuje z walidacją po stronie klienta w MVC, ale nie trzeba polegać wyłącznie na walidacji po stronie klienta. Program Entity Framework zajmie się weryfikacją po stronie serwera dla DataAnnotations lub konfiguracji zastosowanych przy użyciu podejścia Code First z Fluent API.
Widziałeś także wiele punktów rozszerzalności umożliwiających dostosowanie zachowania, niezależnie od tego, czy używasz interfejsu IValidatableObject, czy też sięgasz po metodę DbContext.ValidateEntity. Te dwa ostatnie środki weryfikacji są dostępne za pośrednictwem DbContext, niezależnie od tego, czy używasz przepływu pracy Code First, Model First albo Database First, aby opisać model koncepcyjny.