Code First DataAnnotations摘要與補充

  • 2527
  • 0
  • 2014-06-29

Code First DataAnnotations摘要與補充

適用版本:ASP.Net MVC 3與Entity Frmework 4.1

[Key]
在Code First中,使用EntityKey追蹤Enties的變化,EntityKey的名稱通常是Id或者資料表名稱(如Blog)加上Id(也就是BlogId),倘若並未遵循命名慣例,必須添加Annotation註解:[Key]。

[Required]
[Required]強制要求使用者輸入資料,否則在Client端的驗證將會出現錯誤訊息:The *** field is required。

即使Client端的驗證關閉,Server端的驗證依舊會出現錯誤訊息。

[Required]同時也會要求Not-null。

[MaxLength(m)]
[MinLength(n)]
[MaxLength(m, ErrorMessage="此處是錯誤訊息"),MinLength(n)]

限制輸入的長度最長是m,最短是n,倘若長度超過m,則顯示錯誤訊息。

[NotMapped]
[NotMapped]對應的類別屬性將不會在資料庫中的資料表產生相對應的欄位。

[ComplexType]
在Domain-Driven Design[1][2][3]中,必須透過不同的類別表示Domain Knowledge,因此,類別與類別之間的關係並不局限於一對多的關係,可能包含aggregation與composite,所以不再一定是一個類別對應到一張資料表的綱要,此時必須使用[ComplexType],將特定類別的屬性與聚合屬性的屬性一併對應至同一張資料表。

但是文中的錯誤之處在於屬性之後加入?意指Nullable,而非Non-Nullable。

public DateTime? DateCreated { get; set; } 

Another interesting note is that although the DateCreated property was defined as a non-nullable DateTime in the class, the relevant database field is nullable.

[ConcurrencyCheck]
在連線的資料庫中,資料儲存在記憶體中,[ConcurrencyCheck]無用武之地,但是在離線的狀態下,或者ASP.Net MVC的Stateless之下,當一筆特定的資料顯示在第一個使用者的瀏覽器後,以及在第一個使用者更新資料之前,可能被第二個使用者更新資料,結果第一個使用者在不知道資料已經被第二個使用者更新的情況下,將第二個使用者更新的資料予以覆蓋成為自己更新個資料。
為了避免前述的狀況,在CodeFirst中,當一個類別的屬性之前註記[ConcurrencyCheck],代表資料庫在更新資料時,除了透過主鍵尋找被更新的資料,同時也會檢驗註記[ConcurrencyCheck]的屬性對應的資料表欄位的資料值是否已經被變更,倘若未被(第二個使用者搶先)變更,第一個使用者才能變更資料[4]。

db.Entry(blog).State = System.Data.EntityState.Modified; 
db.Entry(blog).Property(b => b.BloggerName).OriginalValue = originalName; 
db.SaveChanges();
where (([PrimaryTrackingKey] = @4) and ([BloggerName] = @5)) 
@4=1,@5=N'Julie'

前述的程式碼轉換成為sql語法之後,可以發現where子句除了過濾主鍵欄位之外,也會過濾[ConcurrencyCheck]對應的資料表欄位。

[TimeStamp]
不僅用以取代[ConcurrencyCheck],同時限制資料表對映的欄位必須是non-nullable,以及屬性的型別必須是Byte陣列。

資料表

[Table("InternalBlogs")] 
   public class Blog

在CodeFirst中,類別Blog對應的資料表名稱的預設值是Blogs,但是因為前述的註記,所以資料表的實際名稱是InternalBlogs。

資料欄位

[Column(“BlogDescription", TypeName="ntext")] 
   public String Description {get;set;}

在CodeFirst中,類別的屬性名稱/型別對應到資料表的欄位名稱/型別,但是經由前述的註記,資料表的欄位的實際名稱是BlogDescription,型別是ntext。
至於前述的TypeName與Data Type Enumeration[5]並不相同。

DatabaseGenerated
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
當新增一筆資料時,由資料庫產生一個主鍵值。

[DatabaseGenerated(DatabaseGenerationOption.Computed)]
當更新資料時,此一欄位將不會受到更新,但是會產生一個內容值,並且回傳給予應用程式。
database generated可以在新增資料庫時,使用在byte型別與TimeStamp欄位,否則只能指向既有的資料庫,因為CodeFirst不能確認提供資料值給予Computed Column的公式。
(You can use database generated on byte or timestamp columns when code first is generating the database, otherwise you should only use this when pointing to existing databases because code first won't be able to determine the formula for the computed column.)

[DatabaseGenerated(DatabaseGenerationOption.None)]
沒有任何的回應。

關聯屬性InverseProperty和ForeignKey
Association Property可以透過輸入物件名稱再點選屬性名稱的方式瀏覽物件本身與物件的參考屬性以及用以描述與建立資料表和資料表之間的關聯。如下方的public Blog Blog { get; set; }是Association Property[6],代表一對零或者一對一的關係,倘若將Blog型別改為IEnumerable<T>,則是一對多的關係。

public int BlogId { get; set; } 
[ForeignKey("BlogId")] 
public Blog Blog { get; set; }

[ForeignKey("欄位名稱")]
前述的[ForeignKey("BlogId")]則是Navigation Property[7]。

[InverseProperty("欄位名稱")]
當一個Post類別,同時具有二個Person的參考屬性,分別代表發表Post的人與修改Post的人,而一個Person類別,也是同時具有二個IEnumerable<Post>參考屬性,分別代表曾經發表的Post,與曾經修改的Post,此時Code First會產生四個欄位,但是其中有二個欄位是重複而且多餘的欄位,所以可以透過[InverseProperty("欄位名稱")]。

總結
1、DataAnnotation可以進行資料驗證。
2、Code First預設的命名慣例有限,DataAnnotation可以強化甚至修改命名慣例的功能。
3、DataAnnotation不僅可以從無到有產生資料庫,也可以對應至已經存在的資料庫。
4、DataAnnotation僅能滿足一般常見的需求,對於特殊的需求,請參考Code First’s Fluent API[8]。

 

資料來源:
Code First DataAnnotations
http://msdn.microsoft.com/en-us/data/gg193958.aspx

 

補充資料:
[1]Building an MVC 3 App with Code First and Entity Framework 4.1
http://msdn.microsoft.com/en-us/data/gg193958.aspx

[2]Building an MVC 3 App with Code First and Entity Framework 4.1(Video)
http://msdn.microsoft.com/en-us/data/gg715119


參考資料:
[1]Domain-Design Drive
http://en.wikipedia.org/wiki/Domain-driven_design

[2]Domain-Driven Design: Tackling Complexity in the Heart of Software
http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=la_B001KDCO2I_1_1?s=books&ie=UTF8&qid=1400226130&sr=1-1

[3]Domain-Driven Design
https://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web&cd=8&cad=rja&uact=8&ved=0CFIQFjAH&url=http%3A%2F%2Fwww.cs.colorado.edu%2F~kena%2Fclasses%2F5448%2Ff12%2Fpresentation-materials%2Froads.pdf&ei=OdV1U9vaAYK8uATCjoGADQ&usg=AFQjCNHQpgWvPIytTXKhTfRf9q9e4bjbxg
http://www.cs.colorado.edu/~kena/classes/5448/f12/presentation-materials/roads.pdf

[4]Entity Enumeration
http://msdn.microsoft.com/en-us/library/system.data.entitystate.aspx

[5]DataType Enumeration
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.datatype.aspx

[6]MetaDataMember.Association Property
http://msdn.microsoft.com/en-us/library/system.data.linq.mapping.metadatamember.association(v=vs.90).ASPX

[7]Navigation Properties
http://msdn.microsoft.com/en-us/library/vstudio/bb738520(v=vs.100).aspx

[8]Configuring/Mapping Properties and Types with the Fluent API
http://msdn.microsoft.com/en-us/data/jj591617.aspx