EF CodeFirst系列(2)---CodeFirst的数据库初始化
1. CodeFirst的默认约定
1.领域类和数据库架构的映射约定
在介绍数据库的初始化之前我们需要先了解领域类和数据库之间映射的一些约定。在CodeFirst模式中,约定指的是根据领域类(如Student,Grade类)自动配置概念模型的一些默认规则。在上一节的小栗子中,我们没有在领域类中做任何配置,但是EF API帮我们配置了主外键、关系、列的数据类型等,这就是约定在起作用。下表中列除了一些默认的CodeFirst约定:
| 默认规则 | 描述 |
| Schema | EF创建所有的DB对象都放在dbo架构中。dbo.Students |
| Table Name | 实体名的复数,如Student->Students |
| Foreign key |
默认情况,EF会找和主实体的主键名一样名字的列 如果没有的话EF创建一个导航属性名_导航属性主键形式的外键, 如:在dbo.Students表中,外键是Grade_GradeId |
|
列顺序 |
EF创建的数据库列的数据和领域类属性的顺序一致,唯一可能不一致的是会把主键放在第一位 |
|
映射(mapping) |
默认EF会把领域类的所有属性都映射到数据库,可以通过[NotMapped]实现领域类/属性的不映射 |
|
级联删除 |
默认启用所有的类型关系 |
下表显示了C#数据类型到SqlServer数据类型的映射:
| bool | bit |
| byte | tinyint |
| short | smallint |
| int | int |
| long | bigint |
| float | real |
| double | float |
| decimal | decimal(18,2) |
| string | nvrchar(Max) |
| datetime | datetime |
| byte[] | varbinary(Max) |
下图显示了领域类和数据库架构的映射:

2.一些补充
当我们使用导航属性时,EF6中把1对多关系作为默认关系。
注意:EF6中不包含1对1,多对多的默认关系,我们需要自己通过Fluent API或者注释属性进行配置。这些以后会介绍。当EFAPI找不到主键时,CodeFist模式会给这个类创建为复杂类型(Complex Type)。
2.EF确定数据库的名字的方式
前边我们知道了数据库中表名,列名,主外键名是怎么来的,但是数据库的名字是怎么确定的呢?下图展示了数据库初始化的工作流程,在我们创建SchoolContext(继承于DbContext)时,通过给父类的构造函数传值来确定数据库的名字

通过上图,上下文类的父构造函数可以接受如下的参数:
1.无参数
2.数据库名字
3.连接字符串名字
1.无参数
如果不向父构造函数传参,EF会在local SQLEXPRESS中创建数据库,名字是{NameSpace}.{Context Name}。我们上节的栗子中,创建的数据库名字就是:EF6Console.SchoolContext
namespace EF6Console
{
public class SchoolContext : DbContext
{
public SchoolContext():base() { }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<Grade> Grades { get; set; } }
}
2.数据库名字作为参数
namespace EF6Console
{
public class SchoolContext : DbContext
{
public SchoolContext():base("MySchoolDb") { }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<Grade> Grades { get; set; } }
}
如上边的代码所示, 我们把自己想取的名字(如MySchoolDb)传入base即可,EF会在local SQLEXPRESS中帮我们创建一个名字为MySchoolDb的数据库。
3.连接字符串名字
我们也可以通过给base传入连接字符串名字来确定数据库名字,传入连接字符串的格式是:"name=yourConnectionString",注意这个是固定格式,如果不加“name=”的话,EF会认为我们传入的是数据库名字。
namespace EF6Console
{
public class SchoolContext : DbContext
{
public SchoolContext():base("name=SchoolDbConnectionString") { }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<Grade> Grades { get; set; }
}
}
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="SchoolDbConnectionString"
connectionString="Data Source=.;Initial Catalog=SchoolDB-ByConnectionString;Integrated Security=true"
providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
在上边的SchoolContext类中我们把连接字符串的名字作为参数传入,EF会在app.config或web.config找到连接字符串,获取数据库的名字(SchoolDB-ByConnectionString),然后使用现有的数据库,连接字符串中的数据库不存在就在本地Sql Server数据库中新建一个名为SchoolDB-ByConnectionString的数据库(不一定都要是Sql Server,后边会介绍在MySql中生成数据库)。
3.数据库初始化策略
前边我们已经在运行程序时生成了数据库,也知道了数据库初始化的基本流程,但是有一些新的问题:我们再次运行程序会创建一个新的数据库吗?生产环境怎么去配置?数据库生成后如果我们改变了领域类怎么办?为了解决这些问题我们必须有一个数据库迁移策略。
1.四种初始化器
EF中有四种数据库的初始化器:
1.CreateDatabaseIfNotExists:这是默认的初始化器。这种初始化器在第一次运行程序时会创建数据库,再次运行不会再创建新的数据库。但是如果我们改变了领域类,运行程序时会抛出一个异常,这在前边的小栗子中已经演示过了。
2.DropCreateDatabaseIfModelChanges:如果领域类发生了改变,删除以前的数据库,然后重建一个新的。采用这种初始化器我们不用再担心领域类改变影响数据库架构的问题。
3.DropCreateDatabaseAlways:使用这种初始化器,我们每次运行程序都会删除以前的数据库,重建新的数据库。如果在开发过程中每次都想使用最新的数据库,那么可以采用这种初始化器。
4.Custom DB Initializer:自定义初始化器,如果上边几种都不能满足要求,那么我们可以自己定义一个初始化器。
1.通过代码配置初始化器
使用上边四种任意一个初始化策略我们都要使用Database类,如下:
public class SchoolDBContext: DbContext
{
public SchoolDBContext(): base("SchoolDBConnectionString")
{
Database.SetInitializer<SchoolDBContext>(new CreateDatabaseIfNotExists<SchoolDBContext>()); //Database.SetInitializer<SchoolDBContext>(new DropCreateDatabaseIfModelChanges<SchoolDBContext>());
//Database.SetInitializer<SchoolDBContext>(new DropCreateDatabaseAlways<SchoolDBContext>());
//Database.SetInitializer<SchoolDBContext>(new SchoolDBInitializer());//自定义初始化器
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; }
}
自定义的初始化器SchoolDBInitializer继承以上几种初始化器(这里继承CreateDatabaseIfNotExists),如下:
public class SchoolDbInitializer : CreateDatabaseIfNotExists<SchoolDBContext>
{
protected override void Seed(SchoolDbContext context)
{
base.Seed(context);
}
}
2.通过配置文件配置初始化器
我们可以在配置文件中配置初始化器:
①内置的初始化器
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DatabaseInitializerForType EF6Console.SchoolDBContext, EF6Console"
value="System.Data.Entity.DropCreateDatabaseAlways`1[[EF6Console.SchoolDBContext, EF6Console]], EntityFramework" />
</appSettings>
</configuration>
②自定义初始化器
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DatabaseInitializerForType EF6Console.SchoolDBContext, EF6Console"
value="EF6Console.SchoolDBInitializer, EF6Console" />
</appSettings>
</configuration>
2.关闭初始化器
① 通过代码关闭
public class SchoolDBContext: DbContext
{
public SchoolDBContext() : base("SchoolDBConnectionString")
{
//关闭初始化器
Database.SetInitializer<SchoolDBContext>(null);
}
public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; }
}
② 通过配置文件关闭
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DatabaseInitializerForType EF6Console.SchoolDBContext,EF6Console"
value="Disabled" />
</appSettings>
</configuration>
EF CodeFirst系列(2)---CodeFirst的数据库初始化的更多相关文章
- EF CodeFirst系列(9)---添加初始化数据和数据库迁移策略
1.添加初始化数据(Seed) 我们可以在初始化数据库的过程中给数据库添加一些数据.为了实现初始化数据(seed data)我们必须创建一个自定义的数据库初始化器(DB initializer),并重 ...
- 4.DB Initialization(数据库初始化)[EF Code-First系列]
前面的例子中,我们已经看到了Code-First自动为我们创建数据库的例子. 这里我们将要学习的是,当初始化的时候,Code-First是怎么决定数据库的名字和服务的呢??? 下面的图,解释了这一切! ...
- 20.翻译系列:Code-First中的数据库迁移技术【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/migration-in-code-first.aspx EF 6 Code-First ...
- 2.简单的Code First例子(EF Code-First系列)
现在假想,我们想要为讴歌学校创建一个应用程序,这个程序需要能够来添加或者更新学生,分数,教师还有课程信息. 代替之前我们的做法:先是创建数据库,现在我们不这么做,我们先来创建领域类,首先我来创建两个简 ...
- 3.Code-First 约定(EF Code-First系列)
前面,我们已经了解了Code-First利用领域类,怎么为我们创建数据库的简单示例.现在我们来学习一下Code-First约定吧. 什么是约定 约定说白了,就是基于一套规矩办事,这里就是基于你定义好的 ...
- 10.Configure One-to-Many(配置一对多关系)【Code-First系列】
现在,我们将学习怎么配置一对多的关系. Visit Entity Relationship section to understand how EF manages one-to-one, one-t ...
- 9.Configure One-to-One(配置一对一关系)【Code-First系列】
现在,开始学习怎么配置一对一的关系,众所周知,一对一的关系是:一个表中的主键,在另外一个表中,同时是主键和外键[实际上是一对零或者一对一]. 请注意:一对一的关系,在MS SQL Server中,技术 ...
- 11.Configure Many-to-Many(配置多对多关系)【Code-First系列】
现在学习EF Code-First多对多的配置. 这里我们举例:学生和班级实体,一个学生可以选修多个课程,多个学生也可以选修同一个课程. 一.使用数据注解特性,配置多对多的关系 using Syste ...
- 8.Fluent API in Code-First【Code-First系列】
在前面的章节中,我们已经看到了各种不同的数据注解特性.现在我们来学习一下Fluent API. Fluent API是另外一种配置领域类的方式,它提供了更多的配置相比数据注解特性. Mappings[ ...
- 5.翻译系列:EF 6中数据库的初始化(EF 6 Code-First 系列)
原文地址:http://www.entityframeworktutorial.net/code-first/database-initialization-in-code-first.aspx EF ...
随机推荐
- LeetCode算法题-Next Greater Element I(Java实现)
这是悦乐书的第244次更新,第257篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第111题(顺位题号是496).你有两个数组(没有重复)nums1和nums2,其中nu ...
- IDEWorkspaceChecks.plist文件是干什么用的?
在提交PR的时候,无意间发现了在xcworkspace/xcshareddata中多了一个名为IDEWorkspaceChecks.plist的文件.自己并没有手动创建此文件,在网上查了一下,最终对其 ...
- 利用BLKTRACE分析IO性能
在Linux系统上,如果I/O发生性能问题,有没有办法进一步定位故障位置呢?iostat等最常用的工具肯定是指望不上的,[容易被误读的iostat]一文中解释过await表示单个I/O所需的平均时间, ...
- 记一个bug
就在刚刚,测试叫我去看一个问题,有用户反应,在业务页面,出现了一部分重复的内容,而且点击按钮弹窗里,出现了只有个title,没有body的情况. 事情的现象就是这样.然后我就开始着手找原因了.首先声明 ...
- 利用XShell上传、下载文件(使用sz与rz命令)
XSHELL工具上传文件到Linux以及下载文件到本地(Windows) Xshell很好用,然后有时候想在windows和linux上传或下载某个文件,其实有个很简单的方法就是rz,sz.首先你 ...
- 《JAVA程序设计》_第四周学习总结
一.本周学习内容 1.子类与父类--5.1知识 在类的声明中用关键字extends来定义一个类的子类,格式如下: class 子类名 extends 父类名 { ... } 2.子类的继承性--5.2 ...
- day9-基础函数的学习(四)
这几天一直赶着写写作业,博客的书写又落下了,要加油鸭,开写 今日份目录 1.内置函数 2.递归函数 开始今日份总结 1.内置函数 内置函数就是python内部包含的函数,总计有68种,不过有些事真的天 ...
- 【转】从源码分析Handler的postDelayed为什么可以延时?
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/aliankg/article/details/70842494Thread/Hander/Loope ...
- Skyline 7 版本TerraExplorer Pro二次开发快速入门
年底了,给大家整理了一下Skyline 7版本的二次开发学习初级入门教程,献给那些喜欢学习的年轻朋友. 我这整理的是Web控件版本的开发示例,里面页面代码保存成html,都可以直接运行的. 测试使用的 ...
- 如何在Eclipse中Debug调试Java代码
背景 有的时候你想debug调试Java的源代码,就想试图在Java源代码中设置断点,在Eclipse中常常会出现Unable to insert breakpoint Absent Line Num ...