Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Megjegyzés:
CSAK EF4.1 – Az ezen az oldalon tárgyalt funkciók, API-k stb. az Entity Framework 4.1-ben lettek bevezetve. Ha egy korábbi verziót használ, az információk egy része vagy egésze nem érvényes.
Az oldalon található tartalom julie Lerman (<http://thedatafarm.com>) eredetileg írt cikkéből származik.
Az Entity Framework Code First lehetővé teszi, hogy saját tartományosztályokkal képviselje azt a modellt, amelyre az EF a lekérdezési, változáskövetési és frissítési függvények végrehajtásához támaszkodik. A Code First egy olyan programozási mintát használ, amely a "konvenció a konfiguráció felett" elvet alkalmazza. A Code First feltételezi, hogy az osztályok az Entity Framework konvencióit követik, és ebben az esetben automatikusan elvégzik a feladatát. Ha azonban az osztályok nem követik ezeket a konvenciókat, konfigurálásokat adhat az osztályokhoz, hogy az EF-nek megadják a szükséges információkat.
A Code First kétféleképpen adja hozzá ezeket a konfigurációkat az osztályokhoz. Az egyik a DataAnnotations nevű egyszerű attribútumokat használja, a második pedig a Code First Fluent API-ja, amely lehetővé teszi a konfigurációk imperatív leírását a kódban.
Ez a cikk a DataAnnotations (a System.ComponentModel.DataAnnotations névtérben) használatára összpontosít az osztályok konfigurálásához – kiemelve a leggyakrabban szükséges konfigurációkat. A DataAnnotation-t számos .NET-alkalmazás is értelmezi, például ASP.NET MVC, amely lehetővé teszi ezeknek az alkalmazásoknak, hogy ugyanazokat a széljegyzeteket használják az ügyféloldali érvényesítéshez.
A modell
Bemutatom a Code First DataAnnotations-t egy egyszerű osztálypártal: Blog és Post.
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { 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; }
}
Ahogy vannak, a Blog és a Post osztályok kényelmesen követik a kód első konvencióját, és nem igényelnek finomhangolásokat az EF kompatibilitásának engedélyezéséhez. A széljegyzetekkel azonban további információkat is megadhat az EF számára az osztályokról és az adatbázisról, amelyekhez megfeleltetik őket.
Key
Az Entity Framework minden olyan entitásra támaszkodik, amely rendelkezik az entitáskövetéshez használt kulcsértékekkel. A Code First egyik konvenciója az implicit kulcstulajdonságok; A Code First egy "Id" nevű tulajdonságot keres, vagy az osztálynév és az "Azonosító" kombinációját, például a "BlogId" tulajdonságot. Ez a tulajdonság az adatbázis elsődleges kulcsoszlopára lesz megfeleltetve.
A Blog és a Post osztály is követi ezt az egyezményt. Mi lenne, ha nem így lenne? Mi a teendő, ha a Blog a PrimaryTrackingKey nevet használta helyette, vagy akár a foo nevet? Ha a kód először nem talál olyan tulajdonságot, amely megfelel ennek a konvenciónak, kivételt fog okozni az Entity Framework azon követelménye miatt, hogy kulcstulajdonságra van szüksége. A kulcsjegyzet használatával megadhatja, hogy melyik tulajdonságot használja EntityKeyként.
public class Blog
{
[Key]
public int PrimaryTrackingKey { get; set; }
public string Title { get; set; }
public string BloggerName { get; set;}
public virtual ICollection<Post> Posts { get; set; }
}
Ha a kód első adatbázis-létrehozási funkcióját használja, a blogtábla egy PrimaryTrackingKey nevű elsődleges kulcsoszlopot fog használni, amely alapértelmezés szerint identitásként is van definiálva.
Összetett kulcsok
Az Entity Framework támogatja az összetett kulcsokat – az elsődleges kulcsokat, amelyek több tulajdonságból állnak. Lehet például egy Passport-osztály, amelynek elsődleges kulcsa a PassportNumber és az IssuingCountry kombinációja.
public class Passport
{
[Key]
public int PassportNumber { get; set; }
[Key]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
Ha megkísérli a fenti osztályt használni az EF-modellben, az a következőt InvalidOperationExceptioneredményezné:
Nem lehet meghatározni a Passport típus összetett elsődleges kulcsrendezését. Az összetett elsődleges kulcsok sorrendjének megadásához használja a ColumnAttribute vagy a HasKey metódust.
Az összetett kulcsok használatához az Entity Framework megköveteli a kulcstulajdonságok sorrendjének meghatározását. Ezt az Oszlop jelölés használatával teheti meg a sorrend megadásához.
Megjegyzés:
A rendelés értéke relatív (nem indexalapú), így bármilyen érték használható. Például a 100 és a 200 elfogadható lenne az 1 és a 2 helyett.
public class Passport
{
[Key]
[Column(Order=1)]
public int PassportNumber { get; set; }
[Key]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
Ha összetett idegen kulcsokkal rendelkező entitásokkal rendelkezik, akkor a megfelelő elsődleges kulcstulajdonságokhoz használt oszloprendezést kell megadnia.
Csak az idegen kulcs tulajdonságain belüli relatív sorrendnek kell megegyeznie, a Rendeléshez rendelt pontos értékeknek nem kell egyeznie. A következő osztályban például a 3 és a 4 használható az 1 és a 2 helyett.
public class PassportStamp
{
[Key]
public int StampId { get; set; }
public DateTime Stamped { get; set; }
public string StampingCountry { get; set; }
[ForeignKey("Passport")]
[Column(Order = 1)]
public int PassportNumber { get; set; }
[ForeignKey("Passport")]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
public Passport Passport { get; set; }
}
Szükséges
A Required széljegyzet azt jelzi az EF-nek, hogy egy adott tulajdonságra van szükség.
A Cím tulajdonság kötelező hozzáadása kényszeríti az EF-t (és az MVC-t) annak biztosítására, hogy a tulajdonságban adatok legyenek.
[Required]
public string Title { get; set; }
Ha nincs további kód- vagy korrektúramódosítás az alkalmazásban, az MVC-alkalmazások ügyféloldali ellenőrzést hajtanak végre, akár dinamikusan is létrehoznak egy üzenetet a tulajdonság- és széljegyzetnevekkel.
A Szükséges attribútum a létrehozott adatbázisra is hatással lesz, ha a megfeleltetett tulajdonság nem null értékű. Figyelje meg, hogy a Cím mező "nem null" értékre módosult.
Megjegyzés:
Bizonyos esetekben előfordulhat, hogy az adatbázis oszlopa nem lehet null értékű, még akkor sem, ha a tulajdonság megadása kötelező. Ha például több típushoz használ TPH öröklési stratégiai adatokat, a rendszer egyetlen táblában tárolja. Ha egy származtatott típus tartalmaz egy kötelező tulajdonságot, az oszlop nem tehető nem null értékűvé, mivel a hierarchia nem minden típusának lesz ez a tulajdonsága.
MaxLength és MinLength
Az MaxLength és MinLength attribútumok lehetővé teszik további tulajdonságérvényesítések megadását, mint ahogy a Required esetében is.
Itt található a BloggerName hosszkövetelményekkel. A példa azt is bemutatja, hogyan kombinálhatja az attribútumokat.
[MaxLength(10),MinLength(5)]
public string BloggerName { get; set; }
A MaxLength széljegyzet a tulajdonság hosszának 10-es értékre állításával hatással lesz az adatbázisra.
Az MVC ügyféloldali széljegyzete és az EF 4.1 kiszolgálóoldali széljegyzet egyaránt tiszteletben tartja ezt az ellenőrzést, és ismét dinamikusan létrehoz egy hibaüzenetet: "A BloggerName mezőnek sztringnek vagy tömbtípusnak kell lennie, amelynek maximális hossza "10". Ez az üzenet egy kicsit hosszú. Számos széljegyzet lehetővé teszi, hogy hibaüzenetet adjon meg a ErrorMessage attribútummal.
[MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
public string BloggerName { get; set; }
Az ErrorMessage parancsot a Kötelező megjegyzésben is megadhatja.
Nincs megfeleltetve
A kód első konvenciója azt diktálja, hogy minden támogatott adattípusú tulajdonság szerepel az adatbázisban. De nem mindig ez a helyzet az alkalmazásokban. Lehet például, hogy van egy tulajdonsága a Blog osztályban, amely létrehoz egy kódot a Cím és a BloggerName mezők alapján. Ez a tulajdonság dinamikusan hozható létre, és nem szükséges tárolni. Megjelölhet minden olyan tulajdonságot, amely nem megfeleltethető az adatbázisnak a NotMapped jegyzettel, például ez a BlogCode tulajdonság.
[NotMapped]
public string BlogCode
{
get
{
return Title.Substring(0, 1) + ":" + BloggerName.Substring(0, 1);
}
}
ComplexType
Nem ritka, hogy a tartományentitások több osztályban vannak leírva, majd rétegezve az osztályokat egy teljes entitás leírásához. Hozzáadhat például egy BlogDetails nevű osztályt a modellhez.
public class BlogDetails
{
public DateTime? DateCreated { get; set; }
[MaxLength(250)]
public string Description { get; set; }
}
Figyelje meg, hogy BlogDetails nincs semmilyen kulcstulajdonság. A tartományalapú tervezésben BlogDetails értékobjektumnak nevezzük. Az Entity Framework összetett típusokként hivatkozik az értékobjektumokra. Az összetett típusok önmagukban nem követhetők nyomon.
Az osztály Blog egyik BlogDetails tulajdonsága azonban egy Blog objektum részeként lesz nyomon követve. Ahhoz, hogy a kód először felismerje ezt, meg kell jelölnie az BlogDetails osztályt.ComplexType
[ComplexType]
public class BlogDetails
{
public DateTime? DateCreated { get; set; }
[MaxLength(250)]
public string Description { get; set; }
}
Most hozzáadhat egy tulajdonságot az Blog osztályhoz, amely a BlogDetails bloghoz tartozót jelöli.
public BlogDetails BlogDetail { get; set; }
Az adatbázisban a Blog tábla a blog összes tulajdonságát tartalmazza, beleértve a tulajdonságában BlogDetail található tulajdonságokat is. Alapértelmezés szerint mindegyik előtt szerepel a "BlogDetail" komplex típus neve.
Egyidejűség ellenőrzése
A ConcurrencyCheck széljegyzet lehetővé teszi egy vagy több tulajdonság megjelölését az adatbázis egyidejű ellenőrzéséhez, amikor egy felhasználó szerkeszt vagy töröl egy entitást. Ha már dolgozott az EF Designerrel, ez igazodik a tulajdonság értékének beállításához ConcurrencyModeFixed.
Nézzük meg, hogyan ConcurrencyCheck működik, ha hozzáadjuk a BloggerName tulajdonsághoz.
[ConcurrencyCheck, MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
public string BloggerName { get; set; }
Amikor a SaveChanges-t meghívják, a BloggerName mezőn található ConcurrencyCheck annotáció miatt a tulajdonság eredeti értékét fogják használni a frissítés során. A parancs megkísérli megkeresni a megfelelő sort úgy, hogy nem csak a kulcsértékre, hanem az eredeti értékre BloggerNameis szűr. Az UPDATE parancs kritikus részei, amelyek az adatbázisba lettek küldve, ahol láthatja, hogy a parancs frissíti azt a sort, amelynek PrimaryTrackingKey értéke 1 és BloggerName értéke "Julie", ami az eredeti érték volt, amikor a blogot lekérte az adatbázisból.
where (([PrimaryTrackingKey] = @4) and ([BloggerName] = @5))
@4=1,@5=N'Julie'
Ha időközben valaki módosította a blog bloggernevét, a frissítés sikertelen lesz, és kapni fog egy DbUpdateConcurrencyException-t , amelyet kezelnie kell.
Időbélyeg
Az egyidejűség-ellenőrzéshez gyakrabban használják a rowversion vagy az időbélyegmezőket. A széljegyzet használata ConcurrencyCheck helyett azonban a pontosabb TimeStamp széljegyzetet is használhatja, ha a tulajdonság típusa bájttömb. Először a kód ugyanúgy kezeli a Timestamp és ConcurrencyCheck tulajdonságokat, de biztosítja, hogy a Code First által generált adatbázismezők ne lehessenek null értékűek. Egy adott osztályban csak egy időbélyeg-tulajdonság lehet.
Adja hozzá a következő tulajdonságot a blogosztályhoz:
[Timestamp]
public Byte[] TimeStamp { get; set; }
A kód először létrehoz egy nem null értékű időbélyeg-oszlopot az adatbázistáblában.
Tábla és oszlop
Ha engedélyezi, hogy a Code First hozza létre az adatbázist, érdemes lehet módosítania a létrehozott táblák és oszlopok nevét. A Code First egy meglévő adatbázissal is használható. Nem mindig igaz azonban, hogy a tartomány osztályainak és tulajdonságainak neve megegyezik az adatbázis tábláinak és oszlopainak nevével.
Az osztály neve Blog, és a konvenció szerint a kód először feltételezi, hogy ez egy, a Blogs nevű táblára lesz megfeleltetve. Ha ez nem így van, megadhatja a tábla nevét az Table attribútummal. Itt például a széljegyzet azt adja meg, hogy a tábla neve InternalBlogs.
[Table("InternalBlogs")]
public class Blog
A Column annotáció jobban alkalmas a leképezett oszlop attribútumainak megadására. Megadhat egy nevet, adattípust vagy akár azt a sorrendet is, amelyben egy oszlop megjelenik a táblában. Íme egy példa az Column attribútumra.
[Column("BlogDescription", TypeName="ntext")]
public String Description {get;set;}
Ne keverje össze Oszlop attribútumát TypeName a DataType DataAnnotation tulajdonsággal. A DataType a felhasználói felülethez használt széljegyzet, amelyet a Code First figyelmen kívül hagy.
A következő táblázat a regenerálás után következik. A tábla neve InternalBlogs névre módosult, az Description összetett típus oszlopa pedig most már BlogDescription. Mivel a név a széljegyzetben van megadva, a kód először nem fogja használni azt a konvenciót, hogy az oszlop nevét az összetett típus nevével kezdje.
Adatbázis által generált
Az adatbázis egyik fontos funkciója, hogy képes legyen számítási tulajdonságokkal rendelkezni. Ha a Code First osztályokat olyan táblákra kapcsolja, amelyek számított oszlopokat tartalmaznak, nem szeretné, hogy az Entity Framework ezeket az oszlopokat frissítse. Azt azonban szeretné, hogy az EF visszaadja ezeket az értékeket az adatbázisból az adatok beszúrása vagy frissítése után. Használhatja a DatabaseGenerated jegyzettel az osztály tulajdonságainak megjelölésére az Computed enumerátorral együtt. Más felsorolások a következők: None és Identity.
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime DateCreated { get; set; }
A bájt- vagy időbélyegoszlopokon létrehozott adatbázist akkor használhatja, ha a kód először hozza létre az adatbázist, ellenkező esetben csak akkor érdemes ezt használni, ha meglévő adatbázisokra mutat, mert a kód először nem fogja tudni meghatározni a számított oszlop képletét.
A fentiekben azt olvasta, hogy alapértelmezés szerint egy egész számnak számító kulcstulajdonság identitáskulcsmá válik az adatbázisban. Ez ugyanaz lenne, mint ha DatabaseGenerated-t DatabaseGeneratedOption.Identity-re állítaná be. Ha nem szeretné, hogy identitáskulcs legyen, beállíthatja az értéket a következőre DatabaseGeneratedOption.None: .
Index
Megjegyzés:
CSAK EF6.1 – Az Index attribútum az Entity Framework 6.1-ben lett bevezetve. Ha korábbi verziót használ, az ebben a szakaszban szereplő információk nem érvényesek.
Egy vagy több oszlop indexét az IndexAttribute használatával hozhatja létre. Ha egy vagy több tulajdonsághoz hozzáadja az attribútumot, az EF létrehozza a megfelelő indexet az adatbázisban, amikor létrehozza az adatbázist, vagy a kód első áttelepítése esetén a megfelelő CreateIndex-hívásokat hozza létre.
A következő kód például egy index létrehozását eredményezi az Rating adatbázis táblázatának Posts oszlopában.
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index]
public int Rating { get; set; }
public int BlogId { get; set; }
}
Alapértelmezés szerint az index neve IX_<property név> lesz (IX_Rating a fenti példában). Az index nevét is megadhatja. Az alábbi példa azt határozza meg, hogy az index neve legyen PostRatingIndex.
[Index("PostRatingIndex")]
public int Rating { get; set; }
Alapértelmezés szerint az indexek nem egyediek, de az IsUnique elnevezett paraméter használatával megadhatja, hogy az indexnek egyedinek kell lennie. Az alábbi példa egy egyedi indexet mutat be egy Userbejelentkezési névre.
public class User
{
public int UserId { get; set; }
[Index(IsUnique = true)]
[StringLength(200)]
public string Username { get; set; }
public string DisplayName { get; set; }
}
Több oszlopos indexek
A több oszlopra kiterjedő indexeket ugyanazzal a névvel adhatók meg egy adott tábla több indexjegyzetében. Többoszlopos indexek létrehozásakor meg kell adnia az index oszlopainak sorrendjét. Az alábbi kód például létrehoz egy többoszlopos indexet a Rating és BlogId nevű oszlopokon, amit IX_BlogIdAndRating néven hívnak.
BlogId az index első oszlopa, és Rating a második.
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index("IX_BlogIdAndRating", 2)]
public int Rating { get; set; }
[Index("IX_BlogIdAndRating", 1)]
public int BlogId { get; set; }
}
Kapcsolati attribútumok: InverseProperty és ForeignKey
Megjegyzés:
Ez a lap az Első kódmodell kapcsolatainak adatjegyzetek használatával történő beállításáról nyújt tájékoztatást. Az EF-kapcsolatokról és az adatok kapcsolatok használatával való eléréséről és módosításáról a Kapcsolatok > Navigációs tulajdonságok című témakörben olvashat.*
A kód első konvenciója gondoskodik a modell leggyakoribb kapcsolatairól, de vannak olyan esetek, amikor segítségre van szüksége.
Az Blog osztály kulcstulajdonságának nevének megváltoztatása problémát okozott a Post kapcsolatával.
Amikor az adatbázist létrehozzák, a kód először észleli a BlogId tulajdonságot a Post osztályban, és felismeri azt a konvenció szerint, hogy az osztálynév és az Id megegyezése jelzi, hogy ez egy idegen kulcs a Blog osztályhoz. A blogosztályban azonban nincs BlogId tulajdonság. Az a megoldás, hogy létrehoz egy navigációs tulajdonságot a Post-ban, és a ForeignKey DataAnnotation segítségével irányítja a kódot, hogy megértse, hogyan építse fel a kapcsolatot a két osztály között (a Post.BlogId tulajdonság használatával), valamint hogyan adjon meg korlátozásokat az adatbázisban.
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; }
[ForeignKey("BlogId")]
public Blog Blog { get; set; }
public ICollection<Comment> Comments { get; set; }
}
Az adatbázis kényszere a InternalBlogs.PrimaryTrackingKey és a Posts.BlogId közötti kapcsolatot jeleníti meg.
Ez InverseProperty akkor használatos, ha több kapcsolat van az osztályok között.
Post Az osztályban érdemes nyomon követni, hogy ki írt blogbejegyzést, és ki szerkesztette. Íme két új navigációs tulajdonság a Post osztályhoz.
public Person CreatedBy { get; set; }
public Person UpdatedBy { get; set; }
Emellett hozzá kell adnia a Person tulajdonságok által hivatkozott osztályt is. Az Person osztálynak navigációs tulajdonságai vannak a Post személyhez: egy az összes, általa írt bejegyzéshez és egy másik az összes, általa frissített bejegyzéshez.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public List<Post> PostsWritten { get; set; }
public List<Post> PostsUpdated { get; set; }
}
A kód először nem tudja önállóan egyeztetni a két osztály tulajdonságait. Az adatbázistáblának Posts egy idegen kulccsal kell rendelkeznie a CreatedBy személyhez és egy személyhez UpdatedBy , de a kód először négy idegenkulcs-tulajdonságot hoz létre: Person_Id, Person_Id1, CreatedBy_Id és UpdatedBy_Id.
A problémák megoldásához a InverseProperty széljegyzet használatával megadhatja a tulajdonságok igazítását.
[InverseProperty("CreatedBy")]
public List<Post> PostsWritten { get; set; }
[InverseProperty("UpdatedBy")]
public List<Post> PostsUpdated { get; set; }
Mivel a PostsWritten tulajdonság a Person tudja, hogy ez a Post típusra vonatkozik, létrehozza a kapcsolatot Post.CreatedBy-vel. Hasonlóképpen, PostsUpdated a(z) Post.UpdatedBy-hez csatlakozik. És a kód először nem hozza létre a további idegen kulcsokat.
Összefoglalás
A DataAnnotations nem csak az ügyfél- és kiszolgálóoldali ellenőrzés leírását teszi lehetővé a kód első osztályaiban, hanem lehetővé teszik a kód konvenciókon alapuló előfeltételeinek továbbfejlesztését és kijavítását is. A DataAnnotations használatával nem csak az adatbázisséma-létrehozást hajthatja, hanem a kód első osztályait is leképezheti egy már meglévő adatbázisra.
Bár nagyon rugalmasak, ne feledje, hogy a DataAnnotations csak a leggyakrabban szükséges konfigurációs módosításokat biztosítja a kód első osztályaiban. Az osztályok néhány peremesethez való konfigurálásához tekintse meg a Code First Fluent API alternatív konfigurációs mechanizmusát.