EF 學習系列二 資料庫表的建立和表關係配置(Fluent API、Data Annotations、約定)
上一篇寫了《Entity Farmework領域建模方式 3種程式設計方式》,現在就Code First 繼續學習
1、資料庫表的建立
新建一個MVC的專案,在引用右擊管理NuGet程式包,點選瀏覽搜尋EF安裝,我這裡主要是EF6.0 以上的學習 所以都安裝6.0 以上的版本
接下來在Model資料夾下面建立一個Customer類
public class Customer { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } public DateTime AddTime { get; set; } }
在建立一個繼承EF上下文的類XXDBContext,(個人習慣XX是我的名字拼音縮寫)此上下文是資料庫互動的一箇中間橋樑,我們稱之為會話,並且為為一個模型公開一個DbSet。預設情況下EF連結LocalDB本地資料庫(需要安裝LocalDB例項),我還是手動通過EF上下文派生類的建構函式來配置資料庫連結。下面我註釋的是資料庫初始化策略。我這裡就選擇始終建立資料庫,後面用到配置表關聯與欄位的配置。
public class WYDBContext:DbContext { public WYDBContext(string ConnectionName) : base(ConnectionName) { } public WYDBContext():base("SqlConn") { //預設的初始化器。這種初始化器在第一次執行程式時會建立資料庫,再次執行不會再建立新的資料庫。但是如果我們改變了領域類,執行程式時會丟擲一個異常 //Database.SetInitializer(new CreateDatabaseIfNotExists<WYDBContext>()); //如果領域類發生了改變,刪除以前的資料庫,然後重建一個新的。採用這種初始化器不用再擔心領域類改變影響資料庫架構的問題。 //Database.SetInitializer(new DropCreateDatabaseIfModelChanges<WYDBContext.cs>()); //每次執行程式都會刪除以前的資料庫,重建新的資料庫。如果在開發過程中每次都想使用最新的資料庫,那麼可以採用這種初始化器。 Database.SetInitializer(new DropCreateDatabaseAlways<WYDBContext>()); //禁用資料庫初始化策略 //Database.SetInitializer<WYDBContext>(null); } public DbSet<Customer> Customer { get; set; } }
webconfig配置
<system.web> <compilation debug="true" targetFramework="4.6.1" /> <httpRuntime targetFramework="4.6.1" /> </system.web> <!--資料庫連線--> <connectionStrings> <!--資料庫連線ef字串--> <add name="SqlConn" connectionString="Data Source=地址;Initial Catalog=資料庫名;Persist Security Info=True;User ID=使用者名稱;Password=密碼;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
現在如果直接啟動專案資料庫是不會被建立的,只有呼叫到才會建立,在Home控制器的Index中呼叫,啟動就生成了資料庫
public ActionResult Index() { using (var db =new WYDBContext()) { db.Customer.ToList(); } return View(); }
2、 三者約定之 Code First約定(三者優先順序 Fluent API > Data Annotations > 約定)
上面可已看出表Customer自己生成了主鍵ID。所謂約定,類似於C#中的介面,它是一個規範或者規則。使用Code First基於類定義通過約定來配置概念模型並以此為規則,約定就是基本規則。
Code First根據模型中定義的ID(不區分大小寫),或者是以類名加ID的屬性推斷這樣的屬性為ID,如果為int或者guid型別,那麼主鍵對映成標識列(自增長)。
Model下面在建立一個訂單Order類一個客戶有多個訂單一個訂單隻能屬於某一個客戶這樣客戶與訂單的關係就是一對多
public class Order { public int ID { get; set; } public string Name { get; set; } public decimal Price { get; set; } public string Remark { get; set; } public int CustomerID { get; set; } /// <summary> /// 訂單對應的客戶資訊 /// </summary> public virtual Customer Customer { get; set; } } public class Customer { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } public DateTime AddTime { get; set; } /// <summary> /// 客戶對應的訂單資訊 /// </summary> public virtual IList<Order> Order { get; set; } }
資料庫上下文WYDBContext加上 public DbSet<Order> Order { get; set; } 剛剛加的訂單類,執行起來 如果資料庫刪除不了的 自己閃一下 (在navicat 裡面使用會這樣,我就換在SSMS裡面用)
它也生成了表與表的對應關係,然而string型別的你會發現欄位都是max這肯定不行。接下來看Data Annatations 配置
3、三者約定之 Data Annotations
Data Annotations我的理解就是在欄位類名上面加特性註解來控制欄位屬性的 栗子如下 還是Order與Customer兩張表 記得新增名稱空間using System.ComponentModel.DataAnnotations;跟using System.ComponentModel.DataAnnotations.Schema;
public class Customer { /// <summary> /// ID /// </summary> [Key]//標識次列為主鍵 [Column("Zj", Order = 0, TypeName = "int")]//列名Zj,資料庫序號0,型別int [Required()]//不允許為空 [Display(Name = "Zj")]//顯示名稱,這裡大多都是中文 後面檢視@Html.DisplayNameFor(item=> model.Name)用到 顯示的 public int Zj { get; set; } /// <summary> /// 姓名 /// </summary> [Column("NameWYY", TypeName = "nvarchar")]//我加了WYY看效果 [StringLength(50, ErrorMessage = "{0}長度不能超過50個字元")] [Display(Name = "姓名")] public string Name { get; set; } /// <summary> /// 年齡 /// </summary> [Column("Age", TypeName = "int")] [Display(Name = "年齡")] public int? Age { get; set; }//加了?允許為null /// <summary> /// 郵箱 /// </summary> [Column("Email", TypeName = "nvarchar")] [StringLength(50, ErrorMessage = "{0}長度不能超過50個字元")] [Display(Name = "電子郵箱")] public string Email { get; set; } /// <summary> /// 日期 /// </summary> [Column("AddTime", TypeName = "datetime2")]//如果不定義datetime2新增DateTime.Now就會報錯哦 [Display(Name = "新增日期")] [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]//日期格式化 public DateTime AddTime { get; set; } /// <summary> /// 客戶對應的訂單資訊 /// </summary> public virtual IList<Order> Order { get; set; } } // [NotMapped]//表不對映到資料庫 [Table("Tb_Order")]//表名 public class Order { /// <summary> /// ID /// </summary> [Key] [Column("ID", Order = 0, TypeName = "int")] [Required()] [Display(Name = "ID")] public int ID { get; set; } /// <summary> /// 名稱 /// </summary> [Column("Name", TypeName = "nvarchar")] [StringLength(50, ErrorMessage = "{0}長度不能超過50個字元")] [Display(Name = "名稱")] public string Name { get; set; } /// <summary> /// 價格 /// </summary> [Column("Price")] [Display(Name = "價格")] public decimal? Price { get; set; } /// <summary> /// 備註 /// </summary> [StringLength(3000)]//長度約束 [Column("Remark", TypeName = "nvarchar")]//我加了WYY看效果 [Display(Name = "備註")] public string Remark { get; set; } /// <summary> /// 客戶ID /// </summary> [ForeignKey("Customer")]//外來鍵 public int CustomerID { get; set; } /// <summary> /// 訂單對應的客戶資訊 /// </summary> [ForeignKey("CustomerID")]//外來鍵 public virtual Customer Customer { get; set; } /// <summary> /// 不對映欄位 /// </summary> [NotMapped]//不對映到資料庫 public string XXX { get; set; } }
4、三者約定之 Fluent API
這個就要在派生類重寫OnModelCreating了 少一點 的表還可以在裡面設定各個欄位多了還是對映Map在模型表裡面寫,在OnModelCreatiing註冊模型類就可以了;
public DbSet<Customer> Customer { get; set; } public DbSet<Order> Order { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //TODO 配置對映 modelBuilder.Entity<Customer>().ToTable("CSTo");//資料庫表名 modelBuilder.Entity<Customer>().HasKey(x => x.Zj);//主鍵 modelBuilder.Entity<Customer>().Property(x=>x.AddTime).HasColumnType("DATETIME2");//時間 modelBuilder.Entity<Customer>().Property(x=>x.Age).IsOptional();//為null //HasColumnType("DATETIME2(7)")這種寫是錯的 modelBuilder.Entity<Customer>().Property(x => x.Name).IsRequired().HasColumnType("varchar").HasMaxLength(66);//不為空,型別,長度 //預設情況下不會生成複數的表 如Orders modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Configurations.Add(new OrderMap());//註冊 base.OnModelCreating(modelBuilder); } // [NotMapped]//表不對映到資料庫 [Table("Tb_Order")]//表名 public class Order { ///欄位 } public class OrderMap : EntityTypeConfiguration<Order> { public OrderMap() { //對應資料庫表名 this.ToTable("ORd"); //一個訂單必須對應有一個客戶,客戶一對多(訂單) 使用者表裡面的 CustomerID this.HasRequired(p => p.Customer).WithMany(p => p.Order).HasForeignKey(p => p.CustomerID); this.HasKey(k => k.ID);//主鍵 this.Property(p => p.Name).HasColumnType("VARCHAR").HasMaxLength(50).IsRequired();//Name欄位屬性(varchar,長度50,不為null) this.Property(p => p.Remark).HasColumnType("VARCHAR").HasMaxLength(5000).IsOptional();//Remark(varchar,長度5000,null) this.Property(p => p.Price).HasColumnName("pp");//列名 } }
C#的數值型別對應資料庫如下
●C#中的 int型別預設對映後對應資料庫中的int型別。
● C#中的double型別預設對映後對應資料庫中的float型別
●C#中的float型別預設對映後對應資料庫中的real型別。
●C#中 的decimal型別預設對映後對應資料庫中的decimal(18,2)型別
●C#中 的Int64型別預設對映後對應資料庫中的bigint型別。
一般都是用Data Anntations 跟預設的約定好久沒用記錄一下 用到又來拿 Fluent API也是好久沒複習了 哈哈 有時間在看看書