前言
EF已經發布很久了,也有越來越多的人在使用EF。如果你已經能夠非常熟練的使用EF的功能,那么就不需要看了。本文意在將自己使用EF的方式記錄下來備忘,也是為了給剛剛入門的同學一些指導。看完此文,你應該就學會以CodeFirst的方式操作數據庫了。
本文主要內容
- CodeFirst生成數據庫的流程
- 初始化配置
- 數據庫實體構造技巧
- 主外鍵設置
- decimal精度修改
項目框架搭建
本文所使用的開發工具是vs2015(EF6.1.3)
第一步:新建一個空白項目
第二步:引用EntityFramework
DbContext的初始化配置
DbContext作為操作數據庫的網關,十分重要。我們需要對它進行一些類的初始化操作,例如:解決團隊開發中,多人遷移數據庫造成的修改覆蓋問題。
代碼如下:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data.Entity.ModelConfiguration.Conventions; namespace EFDemo.Core.EF { /// <summary> /// EF訪問數據庫的接口 /// </summary> public class MyDbContext : System.Data.Entity.DbContext { public MyDbContext() : base("EFDemo") { //解決團隊開發中,多人遷移數據庫造成的修改覆蓋問題。 Database.SetInitializer<MyDbContext>(null); //base.Configuration.AutoDetectChangesEnabled = false; ////關閉EF6.x 默認自動生成null判斷語句 //base.Configuration.UseDatabaseNullSemantics = true; } public MyDbContext(System.Data.Common.DbConnection oConnection) : base(oConnection, true) { this.Configuration.LazyLoadingEnabled = true; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //表名不用復數形式 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); //移除一對多的級聯刪除約定,想要級聯刪除可以在 EntityTypeConfiguration<TEntity>的實現類中進行控制 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); //多對多啟用級聯刪除約定,不想級聯刪除可以在刪除前判斷關聯的數據進行攔截 modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); base.OnModelCreating(modelBuilder); } //將實體對象寫在這里,就可以生成對應的數據。 如下: //public DbSet<Demo> Demo { get; set; } } }
項目位置:
Migrations下Configuration類的初始化配置
Configuration類的初始化配置十分重要,我們需要通過配置解決一系列遷移問題。例如:允許自動遷移,自動遷移默認情況下不扔掉列在我們的數據庫中的表。如果我們不希望這樣的行為,我們可以告訴遷移明確允許數據丟失的配置類的AutomaticMigrationDataLossAllowed屬性設置為true。
代碼如下:
namespace EFDemo.Core.Migrations { using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<EFDemo.Core.EF.MyDbContext> { public Configuration() { //允許自動遷移 //不然會報錯Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.You can use the Add-Migration command to write the pending model changes to a code-based migration. //允許自動遷移 AutomaticMigrationsEnabled = true; //自動遷移默認情況下不扔掉列在我們的數據庫中的表。如果我們不希望這樣的行為,我們可以告訴遷移明確允許數據丟失的配置類的AutomaticMigrationDataLossAllowed屬性設置為true。 AutomaticMigrationDataLossAllowed = true; } protected override void Seed(EF.MyDbContext context) { // This method will be called after migrating to the latest version. // You can use the DbSet<T>.AddOrUpdate() helper extension method // to avoid creating duplicate seed data. E.g. // // context.People.AddOrUpdate( // p => p.FullName, // new Person { FullName = "Andrew Peters" }, // new Person { FullName = "Brice Lambson" }, // new Person { FullName = "Rowan Miller" } // ); // } } }
項目位置:
數據庫對應實體對象的定義
CodeFirst模式需要我們先定義實體,然后通過實體生成數據。
通常我們設計數據庫表時,每個表都有(ID,是否刪除,備注,添加人,添加時間,修改人,修改時間)等字段。我們可以用基類處理
基類實體:

using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EFDemo.Core.Entity { public class BaseEntity { } [Serializable] public class BaseEntity<TKey> : BaseEntity { public BaseEntity() { this.AddTime = DateTime.Now; } [Key] [Display(Name = "編號")] public TKey ID { get; set; } [Display(Name = "排序")] [Required(ErrorMessage = "{0}是必填項"), Range(0, int.MaxValue, ErrorMessage = "{0}的范圍是{1}到{2}")] [DefaultValue(0)] public int Sort { get; set; } [Display(Name = "備注")] [MaxLength(256, ErrorMessage = "{0}最大長度{1}")] public string Remark { get; set; } [Display(Name = "是否刪除")] [Required] public bool Deleted { get; set; } public int AddUser { get; set; } [Display(Name = "添加時間")] [DisplayFormat(ApplyFormatInEditMode = true, ConvertEmptyStringToNull = true, DataFormatString = "{0:yyyy-MM-dd HH mm}", HtmlEncode = false, NullDisplayText = "數據無效")] public DateTime AddTime { get; set; } public int ModUser { get; set; } [DisplayFormat(ApplyFormatInEditMode = true, ConvertEmptyStringToNull = true, DataFormatString = "{0:yyyy-MM-dd HH mm}", HtmlEncode = false, NullDisplayText = "數據無效")] public DateTime? ModTime { get; set; } } }
省市區表:

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EFDemo.Core.Entity { /// <summary> /// 省市區 /// </summary> public class Public_Area { [Key] public Guid ID { get; set; } [Display(Name = "父親ID")] public Guid ParentID { get; set; } [Display(Name = "名稱")] [MaxLength(32, ErrorMessage = "{0}最大長度{1}")] public String Name { get; set; } } }
班級表:

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EFDemo.Core.Entity { /// <summary> /// 班級 /// </summary> public class T_Classes : BaseEntity<int> { public T_Classes() { this.T_Student = new List<Entity.T_Student>(); } [InverseProperty("T_Classes")] public virtual List<T_Student> T_Student { get; set; } [Display(Name = "班級名稱")] [Required(ErrorMessage = "{0}是必填項")] [MaxLength(8, ErrorMessage = "{0}最大長度{1}")] public string Name { get; set; } [Display(Name = "人數")] public int Count { get; set; } [Display(Name = "班級經費")] public decimal Money { get; set; } } }
學生表:

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EFDemo.Core.Entity { /// <summary> /// 學生 /// </summary> public class T_Student : BaseEntity<int> { /// <summary> /// 外鍵 /// </summary> [ForeignKey("ClassesID")] public virtual T_Classes T_Classes { get; set; } [Display(Name = "班級ID")] public int ClassesID { get; set; } [Display(Name = "姓名")] [Required(ErrorMessage = "{0}是必填項")] [MaxLength(8, ErrorMessage = "{0}最大長度{1}")] public string Name { get; set; } [Display(Name = "性別")] public bool Sex { get; set; } [Display(Name = "年齡")] public int Age { get; set; } [Display(Name = "電話")] [Required(ErrorMessage = "{0}是必填項")] [RegularExpression(@"^(13[0-9]|15[0-9]|18[0-9])\d{8}$", ErrorMessage = "不是手機號格式")] [MaxLength(11, ErrorMessage = "{0}最大長度{1}")] public string Phone { get; set; } /// <summary> /// 省市縣外鍵 /// </summary> [ForeignKey("ProvinceID")] public virtual Public_Area Public_Area_Province { get; set; } [Display(Name = "省ID")] public Guid ProvinceID { get; set; } [ForeignKey("CityID")] public virtual Public_Area Public_Area_City { get; set; } [Display(Name = "市ID")] public Guid CityID { get; set; } [ForeignKey("CountyID")] public virtual Public_Area Public_Area_County { get; set; } [Display(Name = "縣ID")] public Guid CountyID { get; set; } } }
班級表和學生表是一對多的關系,省市區表和學生表是一對多的關系,同時學生表中有多個省市區表的外鍵。
項目位置:
使用命令生成數據庫
第一步:將實體對象加入到DbContext中,
如下:
//將實體對象寫在這里,就可以生成對應的數據。 如下: //public DbSet<Demo> Demo { get; set; } public DbSet<Public_Area> Public_Area { get; set; } public DbSet<T_Classes> T_Classes { get; set; } public DbSet<T_Student> T_Student { get; set; }
第二步:在EFDemo.Core項目下的App.config文件夾中添加生成數據庫的配置項
配置文件代碼如下:
name="EFDemo"中的EFDemo要和DbContex中的一致。
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <connectionStrings> <!--生成數據庫的連接字符串--> <add name="EFDemo" connectionString="Data Source=.;Initial Catalog=EFDemoDB;User ID=sa;Password=123456;MultipleActiveResultSets=True;Application Name=EntityFramework" providerName="System.Data.SqlClient" /> </connectionStrings> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>
第三步:執行更新命令
啟動項目一定要選擇EFDemo.Core
生成數據庫如下:
主外鍵關系設置
班級和學生一對多關系的設置:
一個表中的多個外鍵是另一個表中的主鍵的情況:學生表和省市縣表
生成的數據庫如下:
需要注意的是:這種情況,在Public_Area表中不能反向設置 public virtual List<T_Student> T_Student ,不然會報錯。
decimal怎么保存四位小數
decimal默認保留兩位小數,我們需要通過如下設置讓其保留四位小數。
在DbContext中配置班級表中的Money字段讓其保留四位小數:
執行Update-Database命令:
結果如下:
使用EF操作數據庫數據
第一步:
讓EFDemo.Web應用EFDemo.Core程序集。
第二步:
配置EFDemo.Web中的webconfig中的數據庫連接字符串:
<connectionStrings> <!--操作數據庫的連接字符串--> <add name="EFDemo" connectionString="Data Source=.;Initial Catalog=EFDemoDB;User ID=sa;Password=123456;MultipleActiveResultSets=True;Application Name=EntityFramework" providerName="System.Data.SqlClient" /> </connectionStrings>
第三步:
使用EF向數據庫添加數據
public ActionResult Index() { using (var db = new Core.EF.MyDbContext()) { Public_Area area1 = new Public_Area() { ID = Guid.NewGuid(), Name = "河南", ParentID = Guid.NewGuid() }; db.Public_Area.Add(area1); Public_Area area2 = new Public_Area() { ID = Guid.NewGuid(), Name = "鄭州", ParentID = area1.ID }; db.Public_Area.Add(area2); Public_Area area3 = new Public_Area() { ID = Guid.NewGuid(), Name = "新鄭", ParentID = area2.ID }; db.Public_Area.Add(area3); //添加測試數據 T_Classes classes = new T_Classes() { Name = "高中三班", Money = 2000 }; db.T_Classes.Add(classes); T_Student student = new T_Student() { ClassesID = classes.ID, Name = "張三", Phone = "15236265820", Sex = true, ProvinceID = area1.ID, CityID = area2.ID, CountyID = area3.ID, }; db.T_Student.Add(student); db.SaveChanges(); } return View(); }
第四步:
查看數據庫數據
Demo完整代碼下載