运维开发网

使用CodeFirst模式管理数据库

运维开发网 https://www.qedev.com 2022-08-29 20:19 出处:网络
本文详细讲解了EntityFramework使用CodeFirst模式管理数据库的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 一、管理数据库连接1、使用配置文件管理连接

本文详细讲解了EntityFramework使用CodeFirst模式管理数据库的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 一、管理数据库连接1、使用配置文件管理连接之约定

在数据库上下文类中,如果我们只继承了无参数的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{ /// lt;summarygt; /// 继承无参数的DbContext /// lt;/summarygt; public class SampleDbEntities :DbContext { public SampleDbEntities() { // 数据库不存在时创建数据库 Database.CreateIfNotExists(); } }}

配置文件中定义的连接字符串如下:

lt;connectionStringsgt; lt;add name="SampleDbEntities" connectionString="Data Source=.;Initial Catalog=TestDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /gt;lt;/connectionStringsgt;

定义的连接字符串中name的值与创建的数据库上下文类的类名相同,因此EF将使用连接字符串来执行数据库操作。会发生什么?

运行程序,程序类定义如下:

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会寻找我们的数据库上下文类,即ldquoSampleDbEntitiesrdquo,并在配置文件中查找同名的连接字符串,然后它会使用连接字符串计算应该使用哪个数据库提供者,然后检查数据库位置,然后在指定位置创建一个名为TestDb.mdf的数据库文件,并根据连接字符串的初始目录属性创建一个名为TestDb的数据库。创建的数据库结构如下:


查看创建的数据库,你会发现只有一个迁移记录表。

2、使用已经存在的ConnectionString

如果我们已经有了一个定义数据库的位置和名称的ConnectionString,并且我们希望在数据库上下文类中使用这个连接字符串,则连接字符串如下所示:

lt;connectionStringsgt; lt;add name="AppConnection" connectionString="Data Source=.;Initial Catalog=TestDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /gt;lt;/connectionStringsgt;

以上面创建的数据库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 DbSetlt;Studentgt; 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与数据库上下文类名同名,将使用此同名的连接字符串。无论我们如何更改传入连接字符串的名称,都无济于事。也就是说,与数据库上下文类名同名的连接字符串具有更高的优先级。(即合同大于配置)

3、使用已经存在的连接

通常在一些老项目中,我们只在项目的某一部分先使用EF代码。同时,我们希望将现有的数据库连接用于数据上下文类。如果我们想要实现这一点,我们可以将连接对象传递给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 DbSetlt;Studentgt; Students { get; set; } }}

注意这里的contextOwnsConnection参数。之所以作为false传入上下文,是因为是从外部传入的。当上下文超出范围时,有人可能想要使用连接。如果传入true,一旦上下文超出范围,数据库连接就会关闭。

程序类定义如下:

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代码优先应用时,EF会做以下事情:
1。检查正在使用的DbContext类。
2。查找此上下文类使用的connectionString。
3。找到领域实体,提取与模式相关的信息。
4。创建一个数据库。
5。将数据插入系统。

一旦提取了模式信息,EF将使用数据库初始化器将模式信息推送到数据库。数据库初始化器有许多可能的策略。EF的默认策略是如果数据库不存在就重新创建。如果存在,请使用现有的数据库。当然,有时我们可能需要覆盖默认策略。可以使用的数据库初始化策略如下:

createdatabasinotexists:createdatabasinotexists:顾名思义,如果数据库不存在,那么重新创建;否则,使用现有的数据库。如果从域模型中提取的模式信息与实际的数据库模式不匹配,将会抛出一个异常。

DropCreateDatabaseAlways:如果使用该策略,每次程序运行时,数据库都将被销毁。这在开发周期的早期阶段(比如设计领域实体)和从单元测试的角度来看通常是有用的。

DroreateDatabaseIfModelChanges:这种策略意味着如果域模型发生变化(特别是当从域实体中提取的模式信息与实际的数据库模式信息不匹配时),以前的数据库(如果存在的话)将被销毁,并创建一个新的数据库。

MigrateDatabaseToLatestVersion:如果使用这个初始化器,那么每当实体模型更新时,EF将自动更新数据库模式。这里非常重要的一点是,这种策略可以在不丢失数据或更新现有数据库中的现有数据库对象的情况下更新数据库模式。MigrateDatabaseToLatestVersion初始值设定项仅在EF4.3中可用。

1、设置初始化策略

默认情况下,EF使用CreateDatabaseIfNotExists作为默认的初始化器。如果您想覆盖这个策略,您需要使用数据库。DbContext类的构造函数中的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.SetInitializerlt;SampleDbEntitiesgt;(new DropCreateDatabaseIfModelChangeslt;SampleDbEntitiesgt;()); } // 添加到数据上下文中 public Virtual DbSetlt;Studentgt; Students { get; set; } }}

这样,每当创建一个上下文类时,数据库。将调用SetInitializer()方法,并将数据库初始化策略设置为dropcreateDatabaseIfModelChanges。

学生域实体类中添加了两个新属性:电子邮件和地址:

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; } }}

程序类定义如下:

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传递给数据库。SetInitlalizer()方法,如下所示:

public SampleDbEntities(): base("name=AppConnection"){Database.SetInitializerlt;SampleDbEntitiesgt;(null);}2、填充种子数据

到目前为止,无论我们选择哪种策略初始化数据库,生成的数据库都是一个空数据库。但是在很多情况下,我们总是希望在数据库创建之后,第一次使用之前插入一些数据。此外,开发阶段可能希望以admin的资格填写一些数据,或者伪造一些数据,以便测试应用程序在特定场景中的表现。

当我们使用DropCreateDatabaseAlways和DropCreateDatabaseIfModelChanges初始化策略时,插入种子数据是非常重要的,因为每次运行应用程序都要重新创建数据库,而且每次创建数据库后都要手动插入数据,非常繁琐。接下来,让我们看看如何在创建数据库后使用EF插入种子数据。

为了将一些初始化数据插入数据库,我们需要创建一个满足以下条件的数据库初始化器类:

1.从现有的数据库初始化器类中派生数据。
2。在数据库创建期间设定种子。

下面演示了如何初始化种子数据。

1、定义领域实体类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; } }}2、创建数据库上下文

使用EF的代码优先方法为上述模型创建一个数据库上下文:

public class SampleDbEntities : DbContext{ public Virtual DbSetlt;Employeegt; Employees { get; set; }}3、创建数据库初始化器类

假设我们使用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{ /// lt;summarygt; /// 数据库初始化器类 /// lt;/summarygt; public class SeedingDataInitializer : DropCreateDatabaseAlwayslt;SampleDbEntitiesgt; { /// lt;summarygt; /// 重写DropCreateDatabaseAlways的Seed方法 /// lt;/summarygt; /// lt;param name="context"gt;lt;/paramgt; protected override void Seed(SampleDbEntities context) { for (int i = 0; i lt; 6; i++) { var employee = new Employee { FirstName="测试"+(i+1), LastName="工程师" }; context.Employees.Add(employee); } base.Seed(context); } }}

上面的代码通过for循环创建了六个Employee对象,并将它们添加到数据库上下文类的Employees集合属性中。这里值得注意的是,我们没有调用DbContext。SaveChanges()方法,因为它将在基类中自动调用。

4、将数据库初始化器类用于数据库上下问类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.SetInitializerlt;SampleDbEntitiesgt;(new SeedingDataInitializer()); } // 领域实体添加到数据上下文中 public Virtual DbSetlt;Employeegt; Employees { get; set; } }}5、Main方法中访问数据库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(); } }}6、运行程序,查看结果


查看数据库


种子数据填充完成。

7、使用数据迁移的方式填充种子数据

数据迁移的方法将生成配置类,其定义如下:

namespace DataMigration.Migrations{ using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfigurationlt;DataMigration.SampleDbEntitiesgt; { 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 DbSetlt;Tgt;.AddOrUpdate() helper extension method // to avoid creating duplicate seed data. } }}

覆盖配置类的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 : DbMigrationsConfigurationlt;DataMigration.SampleDbEntitiesgt; { 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 DbSetlt;Tgt;.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使用代码优先模式管理数据库的文章到此结束。

0

精彩评论

暂无评论...
验证码 换一张
取 消