介紹
ADO.NET Entity Framework 4.1 的新增功能:Code First
示例
Web.config
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<!--
需要將 Persist Security Info 設置為 True,以便保存密碼信息
因為 Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>()); 在判斷 Code First 與數據庫結構是否一致時需要連接 master 庫
-->
<add name="MyConnection" providerName="System.Data.SqlClient" connectionString="server=.;database=MyDB;uid=sa;pwd=111111;Persist Security Info=True" />
</connectionStrings>
</configuration>
Global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Data.Entity;
using EF41.CodeFirst;
namespace EF41
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
// 當 Code First 與數據庫結構不一致時,刪除原數據庫,根據 Code First 重新生成新的數據庫結構
Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());
// 什麼都不做
// Database.SetInitializer<MyContext>(null);
}
}
}
Category.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace EF41.CodeFirst
{
public class Category
{
public string CategoryId { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
Product.cs
/*
* 實體到數據庫結構的映射是通過默認的約定來進行的,如果需要修改的話,有兩種方式,分別是:Data Annotations 和 Fluent API
* 以下介紹通過 Data Annotations 來修改實體到數據庫結構的映射(通過 Fluent API 來修改實體到數據庫結構的映射的方法參見 MyContext.cs 文件)
* Table - 指定實體所對應的數據庫的表名,不指定則對應的表名為類名的復數形式
* Key - 指定是否是主鍵,不指定則 Code First 會將名為“Id”或“<類名>Id”的字段推斷為主鍵,且如果它的類型是"int"或"long"或"short"的話,則會在數據庫中默認注冊為 identity 字段。注:主鍵的推斷與大小寫無關
* DatabaseGenerated - 指定字段的值在數據庫中的生成方式
* DatabaseGeneratedOption.None - 不做任何處理
* DatabaseGeneratedOption.Identity - 標識列
* DatabaseGeneratedOption.Computed - 計算列
* Required - 指定為必填字段,即指定數據庫對應的列不允許為 null 值
* MaxLength - 指定字段的最大長度,未指定則為 max
* StringLength - 指定字段的長度范圍
* Column - 指定字段所對應的數據庫中的列名,默認情況下數據庫中的列名同 Code First 中的字段名
* NotMapped - 沒有對應關系,即此字段不會在數據庫中生成對應的列
* Timestamp - 指定對應的數據庫中的列的類型為 datetime
* ForeignKey - 指定外鍵的名稱,默認情況下與導航屬性的主鍵名稱相同的字段會自動被標記為外鍵
* InverseProperty - 指定導航屬性的反轉屬性,默認情況下按實體的互相引用自行推斷
* 所謂“反轉屬性”,按本例看就是 Product.Category 的反轉就是 Category.Products
* ComplexType - 復雜類型,如果字段類型為一個實體類,則此字段會被自動標記為復雜類型,被標記為復雜類型的字段為必填字段,參見 Price.cs 文件
* Timestamp - 將 Code First 中的類型為 byte[] 的字段對應到數據庫中的類型為 timestamp 的列
* ConcurrencyCheck - 指定字段為用於樂觀並發檢查的字段,為了簡單,建議同時將此字段也標記為 Timestamp
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace EF41.CodeFirst
{
[Table("CategoryProduct")]
public class Product
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
[Required]
[MaxLength(128)] // [StringLength(128, MinimumLength = 16)]
[Column("ProductName")]
public string Name { get; set; }
[NotMapped]
public string Comment { get; set; }
public DateTime CreateTime { get; set; }
public Price Price { get; set; }
[ConcurrencyCheck]
[Timestamp]
public byte[] TimeStamp { get; set; }
public string CategoryId { get; set; }
[ForeignKey("CategoryId")]
[InverseProperty("Products")]
public virtual Category Category { get; set; }
}
}
Price.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace EF41.CodeFirst
{
// 以本例來說,對應到數據庫的列將被拆為兩個,分別是:Price_Dollar 和 Price_RMB,且均為必填列
[ComplexType]
public class Price
{
public Price()
{
Dollar = null;
RMB = null;
}
public decimal? Dollar { get; set; }
public decimal? RMB { get; set; }
}
}
MyContext.cs
/*
* 需要引用 EntityFramework(版本 4.1)
* 需要引用 System.Data.Entity
* 需要引用 System.ComponentModel.DataAnnotations
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace EF41.CodeFirst
{
// 創建的 Context 要繼承自 DbContext
public class MyContext : DbContext
{
// 構造函數中的參數用於指定 connectionStrings 的 name
// 默認值為 Context 類的類全名,本例為 EF41.CodeFirst.MyContext
public MyContext(string connString)
: base(connString)
{
// DbContextConfiguration.LazyLoadingEnabled - 是否啟用延遲加載,默認值為 true
// true - 延遲加載(Lazy Loading):獲取實體時不會加載其導航屬性,一旦用到導航屬性就會自動加載
// false - 直接加載(Eager loading):通過 Include 之類的方法顯示加載導航屬性,獲取實體時會即時加載通過 Include 指定的導航屬性
this.Configuration.LazyLoadingEnabled = true;
// DbContextConfiguration.AutoDetectChangesEnabled - 是否自動監測變化,默認值為 true
this.Configuration.AutoDetectChangesEnabled = true;
}
// 所有需要關聯到 Context 的類都要類似如下代碼這樣定義
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
// 實體到數據庫結構的映射是通過默認的約定來進行的,如果需要修改的話,有兩種方式,分別是:Data Annotations 和 Fluent API
// 以下介紹通過 Fluent API 來修改實體到數據庫結構的映射(通過 Data Annotations 來修改實體到數據庫結構的映射的方法參見 Product.cs 文件)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>()
.Property(s => s.Name)
.IsUnicode(false) // 指定對應到數據庫的類型為 varchar,IsUnicode(true) 為 nvarchar,默認為 nvarchar
.IsRequired() // 指定對應到數據庫的列為必填列
.HasMaxLength(64); // 指定對應到數據庫的列的最大長度為 64
}
}
}
Demo.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.Entity;
namespace EF41.CodeFirst
{
public partial class Demo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Code First 中通過 DbContext API 實現增刪改查的 Demo
using (var db = new MyContext("MyConnection"))
{
Random random = new Random();
var category = new Category { CategoryId = Guid.NewGuid().ToString(), Name = "software " + random.Next(1, int.MaxValue) };
db.Categories.Add(category);
var product = new Product { Name = "windows " + random.Next(1, int.MaxValue), Category = category, CreateTime = DateTime.Now, Price = new Price() };
var product2 = new Product { Name = "windows " + random.Next(1, int.MaxValue), Category = category, CreateTime = DateTime.Now, Price = new Price() };
db.Products.Add(product);
db.Products.Add(product2);
int recordsAffected = db.SaveChanges();
Response.Write("影響到數據庫的行數:" + recordsAffected.ToString());
/*
* DbContext API 的一些關鍵點
*
* db.Categories.Find() - 通過傳遞主鍵值作為參數查找實體,復合主鍵就傳多個參數
* db.Categories.Add() - 把一個新增的實體添加到上下文
* db.Categories.Attach() - 把一個已存在的實體添加到上下文
* db.Entry(entity).State = System.Data.EntityState.Modified - 修改實體狀態
* db.Categories.AsNoTracking() - 不被 Context 跟蹤,通過 NoTracking 獲取的實體,其狀態是 Detached 狀態。當僅僅是獲取數據的時候可以用,有助於提高效率
* 屬性的相關操作,當屬性改變時,會自動監測實體狀態,即 IsModified = true
* db.Entry(product).Property(p => p.Name).CurrentValue
* db.Entry(product).Property("Name").CurrentValue
* 直接加載(Eager loading)的方法
* db.Categories.Include(p => p.Products.First())
* db.Categories.Include(p => p.Products)
* db.Entry(product).Reference(p => p.Category).Load()
* 使用 sql
* db.Categories.SqlQuery("select * from Categories").ToList() // 有實體的情況
* db.Database.SqlQuery<string>("select Name from Categories").ToList(); // 無實體的情況
* db.Database.ExecuteSqlCommand(sql); // 直接執行 sql
*/
}
}
}
}
OK