Entity Framework使用Code First模式管理数据库

一、管理数据库连接

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
{
    /// <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的数据库。创建的数据库结构如下:

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

2、使用已经存在的ConnectionString

如果我们已经有了一个定义数据库位置和名称的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,那么就会使用这个同名的连接字符串。无论我们对传入的连接字符串名称如何改变,都是无济于事的,也就是说和数据库上下文类名同名的连接字符串优先权更大。(即约定大于配置)

3、使用已经存在的连接

通常在一些老项目中,我们只会在项目中的某个部分使用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才可用。

1、设置初始化策略

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

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的Code First方式对上面的模型创建数据库上下文:

public class SampleDbEntities : DbContext
{
    public virtual DbSet<Employee> 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
{

    /// <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()方法,因为它会在基类中自动调用。

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.SetInitializer<SampleDbEntities>(new SeedingDataInitializer());
        }

        // 领域实体添加到数据上下文中
        public virtual DbSet<Employee> 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、使用数据迁移的方式填充种子数据

使用数据迁移的方式会生成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模式管理数据库的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 使用EF的Code First模式操作数据库

    EF的核心程序集位于System.Data.Entity.dll和System.Data.EntityFramework.dll中.支持CodeFirst的位于EntityFramework.dll中.通常使用NuGet Package Manager来添加这些程序集. 如果没有数据库: 1.先写代码,自动创建数据库. 2.如果代码有变化,自动删除数据库重建,或者是使用迁移功能更改已有数据库. 如果已有数据库: 使用EF PowerTools反向工程生成模型. 下面的示例程序中将通过一个控制台程

  • C#笔记之EF Code First 数据模型 数据迁移

    目录 一.EF的创建 二.修改数据库 一.加数据库字段 二.加数据库 表 EF的基本使用 一.EF的创建二.修改数据库一.加数据库字段二.加数据库 表 一.EF的创建 第一步: 创建一个类库 第二步: 选择类库 第三步:选择ADO.NET 实体数据模型,名称和你数据库名字对应,我的叫LetDB 第四步:出现了窗体 先解释一下这些EF模型: 1.来自数据库的EF设计器: 先有数据库,根据数据库生成模型 2.空EF设计模型: 模型优先,通过设计的模型生成数据库 3.空 Code First 模型:

  • EF使用Code First模式生成单数形式表名

    使用Code-First模式生成数据库时,默认生成的数据库表的名称为类型的复数形式,例如实体类名称是"User",默认生成的数据库表名为“Users”,多数情况下我们并不想生成的数据库表名为复数形式,那么应该如何来控制呢? 当我们想要自定义一些数据库表的生成规则的时候,会重写OnModelCreating()方法,控制生成的表名的单复数形式同样可以在这个方法中完成,实现代码如下: using System; using System.Collections.Generic; using

  • EF使用Code First模式给实体类添加复合主键

    using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Web; namespace MyFirstMvcApp.Models { /// <summary> /// 登录记录 /// </su

  • asp.net mvc CodeFirst模式数据库迁移步骤详解

    利用Code First模式构建好基本的类后,项目也开始搭建完毕并成功运行,而且已经将数据库表结构自动生成了. 但是,我有新的类要加入,有字段需要修改,那怎么办呢,删库,跑路 ? 哈哈 利用数据库迁移,将原有结构不改动,将新建类进行单独建表操作,或者是已有数据库表,改变字段,那就修改表. 迁移步骤: 1.打开程序包管理器控制台:工具->NuGet包管理器->程序包管理器控制台.(当然还有其它方式也可以打开,我比较喜欢这种) 点击后将弹出程序包管理器控制台 极其要注意的是默认项目!!! 2.启动

  • CodeFirst从零开始搭建Asp.Net Core2.0网站

    一步步教大家如何搭建Asp.Net Core2.0网站,以下所有都是建立在.NETCore2.0环境已经搭建好 右键解决方案>新建项目> 选择Web>ASP.NETCoreWeb应用程序(.NET Core) 选择Web应用程序,暂时不选择启用Docker,身份验证选择个人用户账户(会自动生成一系列和用户认证的代码) 随后生代码层次目录如下: 其中会包含身份信息的相关实现,比如相关实体信息(user)之类的,如果想对扩展微软自动的生成的用户实体类,可在Models中的Applicatio

  • Entity Framework使用Code First模式管理数据库

    一.管理数据库连接 1.使用配置文件管理连接之约定 在数据库上下文类中,如果我们只继承了无参数的DbContext,并且在配置文件中创建了和数据库上下文类同名的连接字符串,那么EF会使用该连接字符串自动计算出数据库的位置和数据库名.比如,我们的数据库上下文定义如下: using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; usin

  • Entity Framework使用Code First模式管理视图

    一.什么是视图 视图在RDBMS(关系型数据库管理系统)中扮演了一个重要的角色,它是将多个表的数据联结成一种看起来像是一张表的结构,但是没有提供持久化.因此,可以将视图看成是一个原生表数据顶层的一个抽象.例如,我们可以使用视图提供不同安全的级别,也可以简化必须编写的查询,尤其是我们可以在代码中的多个地方频繁地访问使用视图定义的数据.EF Code First模式现在还不完全支持视图,因此我们必须使用一种变通的方法.这种方法是:将视图真正看成是一张表,让EF定义这张表,然后在删除它,最后再创建一个

  • Entity Framework使用Code First模式管理存储过程

    在EF中使用存储过程和使用视图是很相似的,一般会使用Database对象上的两个方法:SqlQuery和ExecuteSqlCommand.为了从存储过程中读取很多数据行,我们只需要定义一个类,我们会将检索到的所有数据行物质化到该类实例的集合中.比如,从下面的存储过程读取数据: CREATE PROCEDURE [dbo].[SelectBooks] @BookTypeName AS NVARCHAR(10) AS BEGIN select B.Name,B.Author,B.Publicati

  • Entity Framework使用Code First模式管理事务

    一.什么是事务 处理以数据为中心的应用时,另一个重要的话题是事务管理.ADO.NET为事务管理提供了一个非常干净和有效的API.因为EF运行在ADO.NET之上,所以EF可以使用ADO.NET的事务管理功能. 当从数据库角度谈论事务时,它意味着一系列操作被当作一个不可分割的操作.所有的操作要么全部成功,要么全部失败.事务的概念是一个可靠的工作单元,事务中的所有数据库操作应该被看作是一个工作单元. 从应用程序的角度来看,如果我们有多个数据库操作被当作一个工作单元,那么应该将这些操作包裹在一个事务中

  • Entity Framework使用DataBase First模式实现数据库的增删改查

    在上一篇文章中讲解了如何生成EF的DBFirst模式,接下来讲解如何使用DBFirst模式实现数据库数据的增删改查 一.新增数据 新增一个Student,代码如下: static void Add() { using (StudentSystemEntities dbContext = new StudentSystemEntities()) { // 定义Student对象 Student stu = new Student() { StudentName = "花千骨", Sex

  • Entity Framework使用DataBase First模式实现数据库的增删改查

    在上一篇文章中讲解了如何生成EF的DBFirst模式,接下来讲解如何使用DBFirst模式实现数据库数据的增删改查 一.新增数据 新增一个Student,代码如下: static void Add() { using (StudentSystemEntities dbContext = new StudentSystemEntities()) { // 定义Student对象 Student stu = new Student() { StudentName = "花千骨", Sex

  • Entity Framework使用Code First的实体继承模式

    目录 一.TPT继承模式 1.Person类 2.使用数据迁移创建数据库 3.填充数据 二.TPH模式 1.创建有继承关系的实体类 2.创建数据上下文 3.使用数据迁移创建数据库 4.不使用默认生成的区别多张表的类型 5.填充数据 6.查询数据 三.TPC模式 1.创建实体类 2.配置数据上下文 3.使用数据迁移生成数据库 4.填充数据 Entity Framework的Code First模式有三种实体继承模式 1.Table per Type (TPT)继承 2.Table per Clas

  • Entity Framework使用DataBase First模式实现增删改查

    一.新建控制台应用程序,然后右键->添加新建项,选择数据里面的实体数据模型: 然后点击添加 二.选择来自数据库的EF设计器,并点击下一步 三.在实体数据模型向导界面选择要使用的数据连接,或者点击新建连接按钮创建新的连接,这里选择已有的连接,并点击下一步: 四.选择实体框架6.0,点击下一步: 五.选择要操作的表,并点击完成: 六.查看生成的项目结构 自动添加了EntityFramework的引用.同时会在项目的根目录下面生成一个package文件夹: package文件夹里面存放的是与Entit

  • Entity Framework Core对Web项目生成数据库表

    一.引言 这篇文章中我们讲解如何在Web项目中使用EntityFrameworkCore,并生成数据库表,这里以ASP.NET Core WebApi为例讲解.还是采用分层的结构.创建后的项目整体结构如下图所示: 项目结构: EFCoreWeb.API:ASP.NET Core WebApi项目,用来提供Web功能,在项目中会引用EFCoreWeb.Data. EFCoreWeb.Data:类库项目,基于.NET Core的类库.存放的是与EFCore相关的操作. EFCoreWeb.Model

随机推荐