下面解釋每個配置的作用
Table :用於指定生成表的表名、架構信息。
Column :用於指定生成數據表的列信息,如列名、數據類型、順序等。
Key :用於指定任何名稱的屬性作為主鍵列並且默認將此列作為標識列(如果不想默認生成標識可以指定“DatabaseGenerated”屬性的值為“None”),如果不指定此標記屬性,將根據EF默認約定創建主鍵。如上代碼指定“No”為“Employee”的主鍵。
Required :用戶指定非空列,如上面的“BirthDay”創建列之后為“not null”列。
MinLength 、 MaxLength :指定字段長度(此屬性通常可以用戶客戶端驗證),例如上面“Title”定義成了“nvarchar(30)”。
ComplexType :用於標記復雜類型,對於包含復雜類型數據屬性的類在生成數據表時復雜類型中每個屬性都將作為其中一列。
DatabaseGenerated :用於指定數據庫字段生成列,此類EF將不會直接更新。可以指定為計算列、標識列和非數據庫生成列(例如給主鍵列指定此屬性為“None”則不會生成標識列)。需要注意的是如果使用Code First字段生成數據庫,那么此屬性僅僅可以用於byte、timestamp列上,否則請應用在已經存在數據庫的情況下,因為Code First無法判定生成具體計算列的公式(至少目前Code First還不支持公式配置)。
Identity:自增長 None:不處理 Computed:表示這一列是計算列。
NotMapped :用戶指定非映射列,標記此屬性的列將不會在數據庫中生成相應的列,例如上面的“PhotoPath ”沒有在數據庫中生成具體列,實際使用中它可能是通過其他具體規則得到的。
ConcurrencyCheck :用於進行並發檢查,當一個用戶A獲得實體后通常會與數據庫斷開,此時如果另一個用戶B也獲得了實體並進行了修改,那么當A再進行更新時如果進行了“ConcurrencyCheck”標記則會進行並發檢查,並根據原始值判斷該實體是否存在,如果不存在則拋出異常。
TimeStamp :用於指定時間戳列,一個實體只能有一個TimeStamp列。在EF中TimeStamp是另一種並發控制方式,當EF遇到TimeStamp列會自動配置 “ConcurrencyCheck”及“DatabaseGenerated.Computed”來控制並發(通常我們推薦使用此方法)。
timestamp的精度更高,date精確到秒,而timestamp則精確到秒后三位。
ForeignKey :用於指定外鍵列,我們知道按照上面提到的默認約定第三條,當我們在“Order”中定義了“Customer”屬性后,如果定義“CustomerID” 屬性(當然還有其他形式,大家可以按照聲明說的默認約定3進行測試),那么EF會在“Order”表中創建一個“CustomerID”列並建立與“Customer”表的外鍵關系。但是如果像上面定義“CustomerNo”屬性並且不指定“ForeignKey”標記的話將達不到我們的預期,EF將默認創建一個“Customer_CustomerID”列並創建與“Customer”的外鍵約束,同時創建一個“CustomerNo”列。當然解決的方式大家已經看到了那就是給導航屬性“Customer”指定“ForegnKey”標記並且指定外鍵列為“CustomerNo”(注意雖然在“Customer”中不定義“Order的導航屬性”生成的表中也並沒用任何問題,但是我們推薦您定義相應的導航屬性)。
InverseProperty :用於定義多重外鍵關系約束。我們在EF中通過導航屬性定義主外鍵關系,但是當主表中有兩個外鍵約束時可能僅僅通過添加相應的導航屬性就無法完成了,例如上面“Order”中“DeliverPerson”和“CheckPerson”均為“Employee”類型,按我們的預期當然是在生成“Order”表時生成兩個外鍵列並創建與“Employee”外鍵約束關系,但是如果沒有在“Employee”類的“DeliverOrder”和“CheckOrder”屬性上標記 “InverseProperty”屬性EF是無法識別這種關系的(具體結果可以看下圖),當然解決方式就是在對應導航屬性中標記“InverseProperty”並指定對於的列名。
//主鍵Primary key如果類的屬性名為“ID”(不區分大小寫)或類名的后面跟有“ID”,則 Code First 會推斷該屬性是主鍵。如果主鍵屬性的類型為數值或 GUID,則將其配置為標識列。
導航屬性(Navigationproperty)提供了一種在兩個實體類型之間導航關系的方法。針對對象參與到其中的每個關系,各對象均可以具有導航屬性。使用導航屬性,可以 在兩個方向上導航和管理關系,返回引用對象(如果多重性為一或者零或一)或集合(如果多重性為多)
如果依賴實體上的外鍵不能為 Null,則 CodeFirst 對關系設置級聯刪除。如果依賴實體上的外鍵可以為 Null,則Code First 不對關系設置級聯刪除,並且在刪除主體時,會將該外鍵設置為 Null。通過使用 Fluent API,可以覆蓋由約定檢測的多重性和級聯刪除行為。
數據庫創建是由策略來控制的,有如下四種策略:
1. CreateDatabaseIfNotExists:這是默認的策略。如果數據庫不存在,那么就創建數據庫。但是如果數據庫存在了,而且實體發生了變化,就會出現常。
2. DropCreateDatabaseIfModelChanges:此策略表明,如果模型變化了,數據庫就會被重新創建,原來的數據庫被刪除掉了。
3. DropCreateDatabaseAlways:此策略表示,每次運行程序都會重新創建數據庫,這在開發和調試的時候非常有用。
4. 自定制數據庫策略:可以自己實現IDatabaseInitializer來創建自己的策略。或者從已有的實現了IDatabaseInitializer接口的類派生。 如下示例顯示了如何應用數據庫創建策略:
public class UserManContext : DbContext {
public UserManContext() : base("USMDBConnectionString") {
Database.SetInitializer(new CreateDatabaseIfNotExists());
}
}
[MetadataType(typeof(Blog))] //將元數據類與實體分部類關聯
[DisplayName(“博客”)] //描述
[Table("Blogs",Schema="dbo")] //定義表名
public class Blog {
[Key] //主鍵
public int PrimaryTrackingKey { get; set; }
[Required] //不為空
[DefaultValue("默認值")]//另外一種方式類的構造函數中進行賦值Title="默認值";
public string Title { get; set; }
[ConcurrencyCheck,MaxLength(10), MinLength(5)] //長度約束
public string BloggerName { get;set; }
[NotMapped] //注釋來標記不映射到數據庫的所有屬性
public string BlogCode{
get{
returnTitle.Substring(0, 1) + ":" +BloggerName.Substring(0, 1);
}
}
[Timestamp]
publicByte[] TimeStamp { get;set; }
public virtual ICollection Posts { get;set; } // ICollection可用IList/List/Collection
}
public class Post {
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get;set; }
public string Content { get;set; }
[Range(1,100,ErrorMessage=”{0}在{1}與{2}之間”)] //帶參數的錯誤信息
public int SkipNum{get;set;}
public int BlogId { get; set; }
[ForeignKey("BlogId")]
Public virtual ICollectionComments { get; set;}
}
[ComplexType] //復雜類型公用內容集合可做為某一個類的一部分
public class BlogDetails{
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? DateCreated { get;set; }
[MaxLength(250)]
[Column("BlogDescription",TypeName = "ntext")]
public string Description { get;set; }
}
publicclass Person {
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("CreatedBy")]
public ListPostsWritten { get; set;}
[InverseProperty("UpdatedBy")]
Public ListPostsUpdated { get; set;}
}
[InverseProperty]
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int ForeignKeyCurrent { get; set; }
public int ForeignKeyPrevious { get; set; }
[ForeignKey("ForeignKeyCurrent")]
public Standard CurrentStandard { get; set; }
[ForeignKey("ForeignKeyPrevious")]
public Standard PriviousStandard { get; set; }
}
public class Standard
{
public int StandardId { get; set; }
public string StandardName { get; set; }
/// InverseProperty,反屬性特性
[InverseProperty("CurrentStandard")]
public ICollection CurrentStudent { get; set; }
/// InverseProperty
/// InverseProperty,反屬性特性
[InverseProperty("PriviousStandard")]
public virtual ICollection PreviousStudent { get; set; } //virtual延遲加載
}
DbContext 使用DbSet 屬性 Code First 示例中顯示的常見情況是讓 DbContext 為模型實體類型使用公共自動DbSet 屬性。
例如:可使用 IDbSet 接口替代 DbSet
publicclass BloggingContext: DbContext {
public DbSetBlogs { get; set;}
public DbSetPosts { get; set;}
}
DbContext使用只讀set屬性
public DbSetBlogs{
get{
return Set();
}
}
同步FK 和導航屬性之間的更改
DbSet.Add
context.Blogs.Add(blog);
DbSet.Find
var post =context.Posts.Find(主鍵);
DbSet.Remove
DbSet.Local
DbContext.SaveChanges
DbSet.Attach
context.Blogs.Attach(existingBlog);
DbContext.GetValidationErrors
DbContext.Entry
context.Entry(blog).State =EntityState.Added;
context.Entry(existingBlog).State =EntityState.Unchanged;
context.Entry(existingBlog).State =EntityState.Modified;
DbChangeTracker.Entries