Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
ENDAST EF4.1 – De funktioner, API:er osv. som beskrivs på den här sidan introducerades i Entity Framework 4.1. Om du använder en tidigare version gäller inte en del av eller all information
Innehållet på den här sidan är anpassat från en artikel som ursprungligen skrevs av Julie Lerman (https://thedatafarm.com).
Entity Framework tillhandahåller en mängd olika valideringsfunktioner som kan matas in i ett användargränssnitt för validering på klientsidan eller användas för validering på serversidan. När du använder kod först kan du ange verifieringar med hjälp av antecknings- eller fluent API-konfigurationer. Ytterligare valideringar, och mer komplexa, kan anges i kod och fungerar oavsett om din modell kommer från koden först, modellen först eller databasen först.
Modellen
Jag ska demonstrera valideringarna med ett enkelt par klasser: Blogg och 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; }
}
Dataanteckningar
Code First använder anteckningar från System.ComponentModel.DataAnnotations sammansättningen som ett sätt att konfigurera kod första klasserna. Bland dessa anteckningar finns de som tillhandahåller regler som Required, MaxLength och MinLength. Ett antal .NET-klientprogram känner också igen dessa anteckningar, till exempel ASP.NET MVC. Du kan uppnå både validering på klient- och serversidan med dessa anteckningar. Du kan till exempel tvinga egenskapen Blog Title att vara en obligatorisk egenskap.
[Required]
public string Title { get; set; }
Utan ytterligare kod- eller markeringsändringar i programmet utför ett befintligt MVC-program validering på klientsidan, även dynamiskt genom att skapa ett meddelande med hjälp av egenskaps- och anteckningsnamnen.
I metoden för att skicka tillbaka i den här Create-vyn används Entity Framework för att spara den nya bloggen i databasen, men MVC:s validering på klientsidan aktiveras innan programmet når den koden.
Valideringen på klientsidan är dock inte punktsäker. Användare kan påverka funktioner i sin webbläsare eller ännu värre, en hackare kan använda vissa knep för att undvika UI-valideringar. Men Entity Framework identifierar också anteckningen Required och validerar den.
Ett enkelt sätt att testa detta är att inaktivera MVC:s valideringsfunktion på klientsidan. Du kan göra detta i MVC-programmets web.config-fil. Avsnittet appSettings har en nyckel för ClientValidationEnabled. Om du anger den här nyckeln till false förhindrar du att användargränssnittet utför verifieringar.
<appSettings>
<add key="ClientValidationEnabled"value="false"/>
...
</appSettings>
Även om valideringen på klientsidan är inaktiverad får du samma svar i programmet. Felmeddelandet "Fältet Rubrik krävs" visas som tidigare. Förutom nu är det ett resultat av validering på serversidan. Entity Framework utför valideringen på anteckningen Required (innan den ens bryr sig om att skapa ett INSERT kommando för att skicka till databasen) och returnera felet till MVC som visar meddelandet.
Fluent API
Du kan använda api:et fluent i stället för anteckningar för att få samma validering på klientsidan och på serversidan. I stället för att använda Requiredvisar jag dig detta med hjälp av en MaxLength-validering.
Fluent API-konfigurationer tillämpas eftersom kod först skapar modellen från klasserna. Du kan mata in konfigurationerna genom att åsidosätta metoden OnModelCreating för klassen DbContext. Här är en konfiguration som anger att egenskapen BloggerName inte får vara längre än 10 tecken.
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);
}
}
Valideringsfel som genereras baserat på Fluent API-konfigurationer når inte automatiskt användargränssnittet, men du kan samla in det i kod och sedan svara på det.
Här är några undantagshanteringsfelkod i programmets BlogController-klass som fångar upp det valideringsfelet när Entity Framework försöker spara en blogg med ett BloggerName som överskrider maxvärdet på 10 tecken.
[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();
}
}
Verifieringen skickas inte automatiskt tillbaka till vyn, vilket är anledningen till att den ytterligare kod som används ModelState.AddModelError används. Detta säkerställer att felinformationen når vyn, som sedan använder ValidationMessageFor Htmlhelper för att visa felet.
@Html.ValidationMessageFor(model => model.BloggerName)
IValidatableObject
IValidatableObject är ett gränssnitt som finns i System.ComponentModel.DataAnnotations. Även om det inte ingår i Entity Framework-API:et kan du fortfarande använda det för validering på serversidan i dina Entity Framework-klasser.
IValidatableObject tillhandahåller en Validate metod som Entity Framework anropar under SaveChanges eller så kan du anropa dig själv när du vill verifiera klasserna.
Konfigurationer som Required och MaxLength utför validering på ett enda fält.
Validate I metoden kan du ha ännu mer komplex logik, till exempel genom att jämföra två fält.
I följande exempel Blog har klassen utökats för att implementera IValidatableObject och sedan ange en regel som Title och BloggerName inte kan matcha.
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) });
}
}
}
Konstruktorn ValidationResult tar en string som representerar felmeddelandet och en matris med strings som representerar de medlemsnamn som är associerade med valideringen. Eftersom den här verifieringen kontrollerar både Title och BloggerName, returneras båda egenskapsnamnen.
Till skillnad från valideringen som tillhandahålls av Fluent-API:et identifieras det här valideringsresultatet av vyn och undantagshanteraren som jag använde tidigare för att lägga till felet i ModelState är onödigt. Eftersom jag anger båda egenskapsnamnen ValidationResulti visar MVC HtmlHelpers felmeddelandet för båda dessa egenskaper.
DbContext.ValidateEntity
DbContext har en åsidosättande metod med namnet ValidateEntity. När du anropar SaveChangesanropar Entity Framework den här metoden för varje entitet i cacheminnet vars tillstånd inte Unchangedär . Du kan placera valideringslogik direkt här eller till och med använda den här metoden för att anropa till exempel den metod som Blog.Validate lades till i föregående avsnitt.
Här är ett exempel på en ValidateEntity åsidosättning som validerar nya Post för att säkerställa att inläggets rubrik inte redan har använts. Den kontrollerar först om entiteten är ett inlägg och att dess tillstånd är tillagt. Om så är fallet ser det ut i databasen för att se om det redan finns ett inlägg med samma rubrik. Om det redan finns ett befintligt inlägg skapas en ny DbEntityValidationResult .
DbEntityValidationResult rymmer en DbEntityEntry och en ICollection<DbValidationErrors> för en enda entitet. I början av den här metoden instansieras en DbEntityValidationResult och sedan läggs eventuella fel som identifieras till i samlingen 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);
}
}
Utlösande av validering uttryckligen
Ett anrop till SaveChanges utlöser alla valideringar som beskrivs i den här artikeln. Men du behöver inte förlita dig på SaveChanges. Du kanske föredrar att validera någon annanstans i ditt program.
DbContext.GetValidationErrors utlöser alla valideringar, de som definieras av anteckningar eller Fluent-API:et, valideringen som skapats i IValidatableObject (till exempel Blog.Validate) och valideringarna som utförs i DbContext.ValidateEntity metoden.
Följande kod anropar GetValidationErrors i den aktuella instansen av en DbContext.
ValidationErrors grupperas efter entitetstyp i DbEntityValidationResult. Koden itererar först genom de DbEntityValidationResult som returneras av metoden och sedan genom varje DbValidationError inuti.
foreach (var validationResult in db.GetValidationErrors())
{
foreach (var error in validationResult.ValidationErrors)
{
Debug.WriteLine(
"Entity Property: {0}, Error {1}",
error.PropertyName,
error.ErrorMessage);
}
}
Andra saker att tänka på när du använder validering
Här följer några andra saker att tänka på när du använder Entity Framework-validering:
- Lat inläsning inaktiveras under valideringen
- EF verifierar dataanteckningar för icke-mappade egenskaper (egenskaper som inte mappas till en kolumn i databasen)
- Verifieringen utförs när ändringar har identifierats under
SaveChanges. Om du gör ändringar under valideringen är det ditt ansvar att meddela ändringsspåraren -
DbUnexpectedValidationExceptionutlöses om fel inträffar under valideringen - Fasetter som Entity Framework innehåller i modellen (maximal längd, obligatorisk osv.) orsakar validering, även om det inte finns några dataanteckningar i dina klasser och/eller om du använde EF Designer för att skapa din modell
- Prioritetsregler:
- Fluent API-anrop åsidosätter motsvarande dataanteckningar
- Utförandeordning
- Egenskapsverifiering sker före typverifiering
- Typverifiering sker endast om egenskapsverifieringen lyckas
- Om en egenskap är komplex innehåller valideringen även:
- Validering på egenskapsnivå för egenskaperna för komplex typ
- Typnivåvalidering för den komplexa typen, inklusive
IValidatableObjectvalidering av den komplexa typen
Sammanfattning
Validerings-API:et i Entity Framework fungerar mycket bra med validering på klientsidan i MVC, men du behöver inte förlita dig på validering på klientsidan. Entity Framework tar hand om verifieringen på serversidan för DataAnnotationer eller konfigurationer som du har tillämpat med koden först Fluent API.
Du såg också ett antal utökningspunkter för att anpassa beteendet oavsett om du använder IValidatableObject gränssnittet eller använder DbContext.ValidateEntity metoden. Och de två sista valideringssätten DbContextär tillgängliga via arbetsflödet , oavsett om du använder arbetsflödet Code First, Model First eller Database First för att beskriva din konceptuella modell.