3.Code-First 约定(EF Code-First系列)
前面,我们已经了解了Code-First利用领域类,怎么为我们创建数据库的简单示例。现在我们来学习一下Code-First约定吧。
什么是约定
约定说白了,就是基于一套规矩办事,这里就是基于你定义好的领域类,然后根据默认的规矩来配置概念模型。Code-First约定定义在这个命名空间下面:
System.Data.Entity.ModelConfiguration.Conventions
现在来大致浏览一下都有哪些约定吧:
类型发现
在前面的章节中,我们在上下文类中创建了DbSet属性的类集合,然后Code-First会根据这个DbSet属性为我们创建数据表。
codeFirst会为我们包含任何引用类型到这些类集合中,甚至尽管这写引用类型定义在不同的程序集中。
例如:下面的代码中,Student实体包含了Teacher类的引用,然而上下文类总,却并未包含Teacher的类型的DBSet属性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public DateTime DateOfBirth { get; set; }
public byte[] Photo { get; set; }
public decimal Height { get; set; }
public float Weight { get; set; }
public Standard Standard { get; set; }
public Teacher Teacher { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EF1
{
public class Teacher
{
public int TeacherId { get; set; }
public string TeacherName { get; set; }
}
}
数据上下文类中,并没有包含Teacher类型的DbSet属性:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
public class DbContextClass:DbContext
{
public DbContextClass()
: base("ConnectionString")
{ }
public DbSet<Student> Studnets { get; set; }
public DbSet<Standard> Standards { get; set; }
}
}
然后我们运行程序,运行成功之后,打开数据库,发现:
哈哈,是不是报错了?

具体的错误消息是:The model backing the 'DbContextClass' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)
这是因为,我们改了实体,没有开启数据库迁移特性,不过还有另外一种解决办法,我这里,
我们改一下上下文类:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
public class DbContextClass:DbContext
{
public DbContextClass()
: base("ConnectionString")
{ }
public DbSet<Student> Studnets { get; set; }
public DbSet<Standard> Standards { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DbContextClass>());
base.OnModelCreating(modelBuilder);
}
}
}
这里改动的语句:
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DbContextClass>());
意思是,当模型发生改变的时候,就删掉重新创建数据库。
现在我们运行程序,发现就能运行成功了。
然后我们看一下数据库:

多了一个Teacher表。
看一下Teacher的表结构:

然后看一下Student表的表结构:

Code-First可以推断类,尽管上下文中并没有包含Teacher的属性。
所以,我们总结一下类型发现的约定:
- Code-First包含类型定义也就是DbSet属性在上下文类中;Code-First includes types defined as a DbSet property in context class.
- Code-First包含引用类型在实体类中,尽管他们定义在不同的程序集下;Code-First includes reference types included in entity types even if they are defined in different assembly.
- Code-First包含派生类,尽管这个类要定义成DbSet属性。(Code-First includes derived classes even if only the base class is defined as DbSet property.)
主键约定
在之前的例子中,我们看见了Code-First为每个表都创建了主键,这个默认的规则就是,类名Id(大小写不敏感,即不区分大小写)。这个主键的类型可以是任何类型,但是如果主键的类型是numeric 或者GUID,它将会被配置成自增列。
然后,如果你定义主键的属性名字不是Id或者不是类名+ID,然后在运行的时候,就会报错。
我们看看下面的代码就知道了:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
public class Student
{
public int StdID { get; set; }
public string StudentName { get; set; }
public DateTime DateOfBirth { get; set; }
public byte[] Photo { get; set; }
public decimal Height { get; set; }
public float Weight { get; set; }
public Standard Standard { get; set; }
public Teacher Teacher { get; set; }
}
}
改完之后,我们运行程序,就会报错:

具体的错误消息:
One or more validation errors were detected during model generation:
EF1.Student: : EntityType 'Student' has no key defined. Define the key for this EntityType.
Studnets: EntityType: EntitySet 'Studnets' is based on type 'Student' that has no keys defined.
意思是没有定义主键》》》
如果你想要StdID成为主键,可以使用数据注解或者Fluent APIS,我们在后面将会学到。
关系约定
通过导航属性,Code-First能够推断出,两个实体之间的关系,这个导航属性可以是简单的引用类型或者是集合类型,例如,我们在Student实体中定义了Standard导航属性,然后我们在Stanrard实体中定义了Icollection<Student>导航属性,所以,Code-First将会自动为我们创建一对多的关系。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
public class Student
{
public int StdID { get; set; }
public string StudentName { get; set; }
public DateTime DateOfBirth { get; set; }
public byte[] Photo { get; set; }
public decimal Height { get; set; }
public float Weight { get; set; }
/// <summary>
/// 导航属性
/// </summary>
public Standard Standard { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EF1
{
public class Standard
{
public int StandardId { get; set; }
public string StandardName { get; set; }
/// <summary>
/// 集合类型的导航属性
/// </summary>
public ICollection<Student> Students { get; set; }
}
}
然后我们运行程序成功之后,看到数据库:

Code-First约定,为我们创建了外键
<navigation property Name>_<primary key property name of navigation property type> e.g. Standard_StandardId.
外键约定
我们已经知道了,当我们添加一个外键属性的时候,Code--First会我们创建一个外键。推荐做法是,包含一个外键属性的依赖关系。
请看下面的代码;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public DateTime DateOfBirth { get; set; }
public byte[] Photo { get; set; }
public decimal Height { get; set; }
public float Weight { get; set; }
/// <summary>
/// 导航属性
/// </summary>
public Standard Standard { get; set; }
/// <summary>
/// Foriegn key for Standard
/// </summary>
public int StandardId { get; set; }
}
}
这样改动之后,我们在运行程序,发现怎么了?是不是又报错了?

这个是因为,外键表Standard中没有数据。
我们往Standard表中,插入一条数据,然后改一下Main函数里面的代码:再运行程序>>>>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF1
{
class Program
{
static void Main(string[] args)
{
Student stu = , StudentName = , Weight = , DateOfBirth = DateTime.Now,StandardId=1};
using (var db = new DbContextClass())
{
db.Studnets.Add(stu);
db.SaveChanges();
}
Console.WriteLine("添加成功");
Console.ReadKey();
}
}
}
运行之后,是不是没有出错了?
现在让我们再来看一下数据库:

发现什么变化了么?是不是这个外键的名字不再是以外键属性的名字+外键实体的主键ID命名了?
并且你注意到了没有,这个外键属性也是不能为空的?
因为int类型是不能为空的!!
code-Forst可以基于外键属性的【为空性】(nullability )推断出多重性的关系,如果这个属性是可以为空的,然后这个关系可以看成是null,否则的话,就是not null,你可以修改
StandardId属性的类型从int变成Nullable<int>,然后运行程序的话,得到的外键就是可以为空了。
复杂类型约定
Code-First可以为类创建复杂类型,可以不包含主键属性,并且不使用数据注解和Fluent APIs来注册主键
上面这写就是Code-First约定的大概内容了,这些约定可以被打破,通过使用数据注解和Fluent APIs,在EF6中,你可以自定义约定。
附上系列目录:
- 什么是Code First
- 简单的Code First例子
- Code-First 约定
- DB Initialization(数据库初始化)
- Inheritance Strategy(继承策略)
- Configure Domain Classes(配置领域类)
- DataAnnotations(数据注解)
- Fluent API
- Configure One-to-One(配置一对一关系)
- Configure One-to-Many(配置一对多关系)
- Configure Many-to-Many(配置多对多关系)
- Move Configurations(数据迁移)
- DB Initialization Strategy(数据库初始化策略)
- ...待续.....
3.Code-First 约定(EF Code-First系列)的更多相关文章
- Entity Framework 6新特性:全局性地自定义Code First约定
2012年12月11日,Entity Framework已经发布了Entity Framework 6 Alpha2,因项目需要,目前已使用了其中的两个特性,今天就来介绍一下第一个特性:全局性地自定义 ...
- Code First 约定
Code First 约定 借助 Code First,可通过使用 C# 或 Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code Fi ...
- EntityFramWork(3 code First 约定)
Code First 约定 借助 Code First,可通过使用 C# 或 Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code ...
- EF Code First学习系列
EF Model First在实际工作中基本用不到,前段时间学了一下,大概的了解一下.现在开始学习Code First这种方式.这也是在实际工作中用到最多的方式. 下面先给出一些目录: 1.什么是Co ...
- 1.什么是Code First(EF Code First 系列)
EF4.1中开始支持Code First .这种方式在领域设计模式中非常有用.使用Code First模式,你可以专注于领域设计,根据需要,为你一个领域的对象创建类集合,而不是首先来设计数据库,然后来 ...
- 16.翻译系列:EF 6 Code -First中使用存储过程【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/entityframework6/code-first-insert-update-delete-stored ...
- EF和MVC系列文章导航:EF Code First、DbContext、MVC
对于之前一直使用webForm服务器控件.手写ado.net操作数据库的同学,突然来了EF和MVC,好多新概念泉涌而出,的确犹如当头一棒不知所措.本系列文章可以帮助新手入门并熟练使用EF和MVC,有了 ...
- 【EF】Entity Framework 6新特性:全局性地自定义Code First约定
应用场景 场景一:EF Code First默认使用类名作为表名,如果我们需要给表名加个前缀,例如将类名Category映射到表Shop_Category.将Product映射到Shop_Produc ...
- EF Code First 学习笔记:约定配置 Data Annotations+Fluent API
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...
- ASP.NET Web API实践系列02,在MVC4下的一个实例, 包含EF Code First,依赖注入, Bootstrap等
本篇体验在MVC4下,实现一个对Book信息的管理,包括增删查等,用到了EF Code First, 使用Unity进行依赖注入,前端使用Bootstrap美化.先上最终效果: →创建一个MVC4项目 ...
随机推荐
- Chrome必备的扩展
Devtools Terminal ——浏览器终端.牛逼的不得了! LiveReload——为官方 LiveReload 应用程序(Mac 和 Windows)和第三方,例如 guard-livere ...
- [.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图
[.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图 1.UML简介 Unified Modeling Language (UML)又称统 ...
- IEEE754、VAX、IBM浮点型介绍和.NET中互相转换
[题外话] 最近在做C3D文件的解析,好奇怪的是文件中竟然存储了CPU的类型,原本不以为然,结果后来读取一个文件发现浮点数全部读取错误.查了下发现虽然在上世纪80年代就提出了IEEE754要统一浮点数 ...
- Npm install failed with “cannot run in wd”
Linux环境下,root账户,安装某些npm包的时候报下面的错误,例如安装grunt-contrib-imagemin时: Error: EACCES, mkdir '/usr/local/lib/ ...
- .NET 基础 一步步 一幕幕 [.NET 系列预热]
.NET 系列预热 .NET : 一般指.Net Framework框架.一种平台,一种技术. .NET 下的编程语言有C#,F#,VB:在这里我们主要讲述的C#. .NET 一般都能干什么呢? l ...
- mac下tomcat的安装与配置
1.到 apache官方主页 下载 Mac 版本的完整 tar.gz文件包.解压拷贝到 /Library目录下,并命名为Tomcat,其他目录也可. 2.修改目录权限 到终端输入 sudo chm ...
- JAVA基础-输入输出:1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上。
1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上. package Test03; ...
- 【Win 10 应用开发】RTM版的UAP项目解剖
Windows 10 发布后,其实SDK也偷偷地在VS的自定义安装列表中出现了,今天开发人员中心也更新了下载.正式版的SDK在API结构上和以前预览的时候是一样的,只是版本变成10240罢了,所以大家 ...
- OPEN CASCADE Multiple Variable Function
OPEN CASCADE Multiple Variable Function eryar@163.com Abstract. Multiple variable function with grad ...
- 详解 ML2 Core Plugin(II) - 每天5分钟玩转 OpenStack(72)
上一节我们讨论了 ML2 Plugin 解决的问题,本节将继续研究 ML2 的架构. ML2 对二层网络进行抽象和建模,引入了 type driver 和 mechansim driver. 这两类 ...