<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在資料庫上下文類中,如果我們只繼承了無引數的DbContext,並且在組態檔中建立了和資料庫上下文類同名的連線字串,那麼EF會使用該連線字串自動計算出資料庫的位置和資料庫名。比如,我們的資料庫上下文定義如下:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConventionConfigure.EF { /// <summary> /// 繼承無引數的DbContext /// </summary> public class SampleDbEntities :DbContext { public SampleDbEntities() { // 資料庫不存在時建立資料庫 Database.CreateIfNotExists(); } } }
在組態檔中定義的連線字串如下:
<connectionStrings> <add name="SampleDbEntities" connectionString="Data Source=.;Initial Catalog=TestDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
定義的連線字串中name的value值和建立的資料庫上下文類的類名相同,這樣EF會使用該連線字串執行資料庫操作,究竟會發生什麼呢?
執行程式,Program類定義如下:
using ConventionConfigure.EF; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConventionConfigure { class Program { static void Main(string[] args) { using (var context = new SampleDbEntities()) { } Console.WriteLine("建立成功"); Console.ReadKey(); } } }
當執行應用程式時,EF會尋找我們的資料庫上下文類,即“SampleDbEntities”,並在組態檔中尋找和它同名的連線字串,然後它會使用該連線字串計算出應該使用哪個資料庫provider,之後檢查資料庫位置,之後會在指定的位置建立一個名為TestDb.mdf的資料庫檔案,同時根據連線字串的Initial Catalog屬性建立了一個名為TestDb的資料庫。建立的資料庫結構如下:
檢視建立後的資料庫,會發現只有一張遷移記錄表。
如果我們已經有了一個定義資料庫位置和名稱的ConnectionString,並且我們想在資料庫上下文類中使用這個連線字串,連線字串如下:
<connectionStrings> <add name="AppConnection" connectionString="Data Source=.;Initial Catalog=TestDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
以上面建立的資料庫TestDb作為已經存在的資料庫,新新增實體類Student,使用已經存在的ConnectionString查詢資料庫的Student表,Student實體類定義如下:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExistsConnectionString.Model { [Table("Student")] public class Student { public int Id { get; set; } public string Name { get; set; } public string Sex { get; set; } public int Age { get; set; } } }
我們將該連線字串的名字傳入資料庫上下文DbContext的有參建構函式中,資料庫上下文類定義如下:
using ExistsConnectionString.Model; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExistsConnectionString.EF { public class SampleDbEntities : DbContext { public SampleDbEntities() : base("name=AppConnection") { } // 新增到資料上下文中 public virtual DbSet<Student> Students { get; set; } } }
上面的程式碼將連線字串的名字傳給了DbContext類的有參建構函式,這樣一來,我們的資料庫上下文就會開始使用該連線字串了,在Program類中輸出Name和Age欄位的值:
using ExistsConnectionString.EF; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExistsConnectionString { class Program { static void Main(string[] args) { using (var context = new SampleDbEntities()) { foreach (var item in context.Students) { Console.WriteLine("姓名:"+item.Name+" "+"年齡:"+item.Age); } } } } }
執行程式,發現會報下面的錯誤:
出現上面報錯的原因是因為資料庫上下文發生了改變,與現有資料庫不匹配。解決方案:
重新執行程式,結果如下:
注意:如果在組態檔中還有一個和資料庫上下文類名同名的ConnectionString,那麼就會使用這個同名的連線字串。無論我們對傳入的連線字串名稱如何改變,都是無濟於事的,也就是說和資料庫上下文類名同名的連線字串優先權更大。(即約定大於設定)
通常在一些老專案中,我們只會在專案中的某個部分使用EF Code First,同時,我們想對資料上下文類使用已經存在的資料庫連線,如果要實現這個,可將連線物件傳給DbContext類別建構函式,資料上下文定義如下:
using ExistsDbConnection.Model; using System; using System.Collections.Generic; using System.Data.Common; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExistsDbConnection.EF { public class SampleDbEntities :DbContext { public SampleDbEntities(DbConnection con) : base(con, contextOwnsConnection: false) { } public virtual DbSet<Student> Students { get; set; } } }
這裡要注意一下contextOwnsConnection引數,之所以將它作為false傳入到上下文,是因為它是從外部傳入的,當上下文超出了範圍時,可能會有人想要使用該連線。如果傳入true的話,那麼一旦上下文出了範圍,資料庫連線就會立即關閉。
Program類定義如下:
using System; using System.Collections.Generic; using System.Data.Common; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Configuration; using ExistsDbConnection.EF; namespace ExistsDbConnection { class Program { static void Main(string[] args) { // 讀取連線字串 string conn = ConfigurationManager.ConnectionStrings["AppConnection"].ConnectionString; // DbConnection是抽象類,不能直接範例化,宣告子類指向父類別物件 DbConnection con = new SqlConnection(conn); using (var context = new SampleDbEntities(con)) { foreach (var item in context.Students) { Console.WriteLine("姓名:" + item.Name + " " + "年齡:" + item.Age); } } Console.WriteLine("讀取完成"); Console.ReadKey(); } } }
執行程式,結果如下:
首次執行EF Code First應用時,EF會做下面的這些事情:
1、檢查正在使用的DbContext類。
2、找到該上下文類使用的connectionString。
3、找到領域實體並提取模式相關的資訊。
4、建立資料庫。
5、將資料插入系統。
一旦模式資訊提取出來,EF會使用資料庫初始化器將該模式資訊推播給資料庫。資料庫初始化器有很多可能的策略,EF預設的策略是如果資料庫不存在,那麼就重新建立;如果存在的話就使用當前存在的資料庫。當然,我們有時也可能需要覆蓋預設的策略,可能用到的資料庫初始化策略如下:
CreateDatabaseIfNotExists:CreateDatabaseIfNotExists:顧名思義,如果資料庫不存在,那麼就重新建立,否則就使用現有的資料庫。如果從領域模型中提取到的模式資訊和實際的資料庫模式不匹配,那麼就會丟擲異常。
DropCreateDatabaseAlways:如果使用了該策略,那麼每次執行程式時,資料庫都會被銷燬。這在開發週期的早期階段通常很有用(比如設計領域實體時),從單元測試的角度也很有用。
DropCreateDatabaseIfModelChanges:這個策略的意思就是說,如果領域模型發生了變化(具體而言,從領域實體提取出來的模式資訊和實際的資料庫模式資訊失配時),就會銷燬以前的資料庫(如果存在的話),並建立新的資料庫。
MigrateDatabaseToLatestVersion:如果使用了該初始化器,那麼無論什麼時候更新實體模型,EF都會自動地更新資料庫模式。這裡很重要的一點是:這種策略更新資料庫模式不會丟失資料,或者是在已有的資料庫中更新已存在的資料庫物件。MigrateDatabaseToLatestVersion初始化器只有從EF4.3才可用。
EF預設使用CreateDatabaseIfNotExists作為預設初始化器,如果要覆蓋這個策略,那麼需要在DbContext類中的建構函式中使用Database.SetInitializer方法,下面的例子使用DropCreateDatabaseIfModelChanges策略覆蓋預設的策略。資料庫上下文類定義如下:
using InitializationStrategy.Model; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationStrategy.EF { public class SampleDbEntities : DbContext { public SampleDbEntities() : base("name=AppConnection") { // 使用DropCreateDatabaseIfModelChanges策略覆蓋預設的策略 Database.SetInitializer<SampleDbEntities>(new DropCreateDatabaseIfModelChanges<SampleDbEntities>()); } // 新增到資料上下文中 public virtual DbSet<Student> Students { get; set; } } }
這樣一來,無論什麼時候建立上下文類,Database.SetInitializer()方法都會被呼叫,並且將資料庫初始化策略設定為DropCreateDatabaseIfModelChanges。
Student領域實體類新增加Email和Address兩個屬性:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationStrategy.Model { [Table("Student")] public class Student { public int Id { get; set; } public string Name { get; set; } public string Sex { get; set; } public int Age { get; set; } public string Email { get; set; } public string Address { get; set; } } }
Program類定義如下:
using InitializationStrategy.EF; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationStrategy { class Program { static void Main(string[] args) { using (var context = new SampleDbEntities()) { foreach (var item in context.Students) { } } Console.WriteLine("建立成功"); Console.ReadKey(); } } }
執行程式後,資料庫表結構如下:
注意:如果處於生產環境,那麼我們肯定不想丟失已經存在的資料。這時我們就需要關閉該初始化器,只需要將null傳給Database.SetInitlalizer()方法,如下所示:
public SampleDbEntities(): base("name=AppConnection") { Database.SetInitializer<SampleDbEntities>(null); }
到目前為止,無論我們選擇哪種策略初始化資料庫,生成的資料庫都是一個空的資料庫。但是許多情況下我們總想在資料庫建立之後、首次使用之前就插入一些資料。此外,開發階段可能想以admin的資格為其填充一些資料,或者為了測試應用在特定的場景中表現如何,想要偽造一些資料。
當我們使用DropCreateDatabaseAlways和DropCreateDatabaseIfModelChanges初始化策略時,插入種子資料非常重要,因為每次執行應用時,資料庫都要重新建立,每次資料庫建立之後在手動插入資料非常乏味。接下來我們看一下當資料庫建立之後如何使用EF來插入種子資料。
為了向資料庫插入一些初始化資料,我們需要建立滿足下列條件的資料庫初始化器類:
1、從已存在的資料庫初始化器類中派生資料。
2、在資料庫建立期間種子化。
下面演示如何初始化種子資料
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationSeed.Model { [Table("Employee")] public class Employee { public int EmployeeId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } }
使用EF的Code First方式對上面的模型建立資料庫上下文:
public class SampleDbEntities : DbContext { public virtual DbSet<Employee> Employees { get; set; } }
假設我們使用的是DropCreateDatabaseAlways資料庫初始化策略,那麼初始化器類就要從該泛型類繼承,並傳入資料庫上下文作為型別引數。接下來,要種子化資料庫就要重寫DropCreateDatabaseAlways類的Seed()方法,而Seed()方法拿到了資料庫上下文,因此我們可以使用它來將資料插入資料庫:
using InitializationSeed.Model; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationSeed.EF { /// <summary> /// 資料庫初始化器類 /// </summary> public class SeedingDataInitializer : DropCreateDatabaseAlways<SampleDbEntities> { /// <summary> /// 重寫DropCreateDatabaseAlways的Seed方法 /// </summary> /// <param name="context"></param> protected override void Seed(SampleDbEntities context) { for (int i = 0; i < 6; i++) { var employee = new Employee { FirstName="測試"+(i+1), LastName="工程師" }; context.Employees.Add(employee); } base.Seed(context); } } }
上面的程式碼通過for迴圈建立了6個Employee物件,並將它們新增給資料庫上下文類的Employees集合屬性。這裡值得注意的是我們並沒有呼叫DbContext.SaveChanges()方法,因為它會在基礎類別中自動呼叫。
using InitializationSeed.Model; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationSeed.EF { public class SampleDbEntities :DbContext { public SampleDbEntities() : base("name=AppConnection") { // 型別傳SeedingDataInitializer Database.SetInitializer<SampleDbEntities>(new SeedingDataInitializer()); } // 領域實體新增到資料上下文中 public virtual DbSet<Employee> Employees { get; set; } } }
using InitializationSeed.EF; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InitializationSeed { class Program { static void Main(string[] args) { using (var context = new SampleDbEntities()) { foreach (var item in context.Employees) { Console.WriteLine("FirstName:"+item.FirstName+" "+"LastName:"+item.LastName); } } Console.WriteLine("讀取完成"); Console.ReadKey(); } } }
檢視資料庫
種子資料填充完成。
使用資料遷移的方式會生成Configuration類,Configuration類定義如下:
namespace DataMigration.Migrations { using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<DataMigration.SampleDbEntities> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(DataMigration.SampleDbEntities 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. } } }
重寫Configuration類的Seed()方法也可以實現插入種子資料,重寫Seed()方法:
namespace DataMigration.Migrations { using DataMigration.Model; using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<DataMigration.SampleDbEntities> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(DataMigration.SampleDbEntities 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. context.Employees.AddOrUpdate( new Employee { FirstName = "測試1", LastName = "工程師" }, new Employee { FirstName = "測試2", LastName = "工程師" } ); } } }
使用資料遷移,然後檢視資料庫結果:
發現使用資料遷移的方式也將種子資料插入到了資料庫中。
程式碼下載地址:點此下載
到此這篇關於Entity Framework使用Code First模式管理資料庫的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45