EF 学习系列二 数据库表的创建和表关系配置(Fluent API、Data Annotations、约定)
上一篇写了《Entity Farmework领域建模方式 3种编程方式》,现在就Code First 继续学习
1、数据库表的创建
新建一个MVC的项目,在引用右击管理NuGet程序包,点击浏览搜索EF安装,我这里主要是EF6.0 以上的学习 所以都安装6.0 以上的版本

接下来在Model文件夹下面创建一个Customer类
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public DateTime AddTime { get; set; }
}
在创建一个继承EF上下文的类XXDBContext,(个人习惯XX是我的名字拼音缩写)此上下文是数据库交互的一个中间桥梁,我们称之为会话,并且为为一个模型公开一个DbSet。默认情况下EF链接LocalDB本地数据库(需要安装LocalDB实例),我还是手动通过EF上下文派生类的构造函数来配置数据库链接。下面我注释的是数据库初始化策略。我这里就选择始终创建数据库,后面用到配置表关联与字段的配置。
public class WYDBContext:DbContext
{
public WYDBContext(string ConnectionName) : base(ConnectionName) { }
public WYDBContext():base("SqlConn")
{
//默认的初始化器。这种初始化器在第一次运行程序时会创建数据库,再次运行不会再创建新的数据库。但是如果我们改变了领域类,运行程序时会抛出一个异常
//Database.SetInitializer(new CreateDatabaseIfNotExists<WYDBContext>()); //如果领域类发生了改变,删除以前的数据库,然后重建一个新的。采用这种初始化器不用再担心领域类改变影响数据库架构的问题。
//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<WYDBContext.cs>()); //每次运行程序都会删除以前的数据库,重建新的数据库。如果在开发过程中每次都想使用最新的数据库,那么可以采用这种初始化器。
Database.SetInitializer(new DropCreateDatabaseAlways<WYDBContext>()); //禁用数据库初始化策略
//Database.SetInitializer<WYDBContext>(null);
}
public DbSet<Customer> Customer { get; set; }
}
webconfig配置
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" />
</system.web> <!--数据库连接-->
<connectionStrings>
<!--数据库连接ef字符串-->
<add name="SqlConn" connectionString="Data Source=地址;Initial Catalog=数据库名;Persist Security Info=True;User ID=用户名;Password=密码;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>
现在如果直接启动项目数据库是不会被创建的,只有调用到才会创建,在Home控制器的Index中调用,启动就生成了数据库
public ActionResult Index()
{
using (var db =new WYDBContext())
{
db.Customer.ToList();
}
return View();
}

2、 三者约定之 Code First约定(三者优先级 Fluent API > Data Annotations > 约定)
上面可已看出表Customer自己生成了主键ID。所谓约定,类似于C#中的接口,它是一个规范或者规则。使用Code First基于类定义通过约定来配置概念模型并以此为规则,约定就是基本规则。
Code First根据模型中定义的ID(不区分大小写),或者是以类名加ID的属性推断这样的属性为ID,如果为int或者guid类型,那么主键映射成标识列(自增长)。
Model下面在创建一个订单Order类一个客户有多个订单一个订单只能属于某一个客户这样客户与订单的关系就是一对多
public class Order
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Remark { get; set; }
public int CustomerID { get; set; }
/// <summary>
/// 订单对应的客户信息
/// </summary>
public virtual Customer Customer { get; set; }
} public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public DateTime AddTime { get; set; }
/// <summary>
/// 客户对应的订单信息
/// </summary>
public virtual IList<Order> Order { get; set; }
}
数据库上下文WYDBContext加上 public DbSet<Order> Order { get; set; } 刚刚加的订单类,运行起来 如果数据库删除不了的 自己闪一下 (在navicat 里面使用会这样,我就换在SSMS里面用)

它也生成了表与表的对应关系,然而string类型的你会发现字段都是max这肯定不行。接下来看Data Annatations 配置
3、三者约定之 Data Annotations
Data Annotations我的理解就是在字段类名上面加特性注解来控制字段属性的 栗子如下 还是Order与Customer两张表 记得添加命名空间using System.ComponentModel.DataAnnotations;跟using System.ComponentModel.DataAnnotations.Schema;
public class Customer
{
/// <summary>
/// ID
/// </summary>
[Key]//标识次列为主键
[Column("Zj", Order = , TypeName = "int")]//列名Zj,数据库序号0,类型int
[Required()]//不允许为空
[Display(Name = "Zj")]//显示名称,这里大多都是中文 后面视图@Html.DisplayNameFor(item=> model.Name)用到 显示的
public int Zj { get; set; }
/// <summary>
/// 姓名
/// </summary>
[Column("NameWYY", TypeName = "nvarchar")]//我加了WYY看效果
[StringLength(, ErrorMessage = "{0}长度不能超过50个字符")]
[Display(Name = "姓名")]
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary> [Column("Age", TypeName = "int")]
[Display(Name = "年龄")]
public int? Age { get; set; }//加了?允许为null
/// <summary>
/// 邮箱
/// </summary>
[Column("Email", TypeName = "nvarchar")]
[StringLength(, ErrorMessage = "{0}长度不能超过50个字符")]
[Display(Name = "电子邮箱")]
public string Email { get; set; }
/// <summary>
/// 日期
/// </summary>
[Column("AddTime", TypeName = "datetime2")]//如果不定义datetime2添加DateTime.Now就会报错哦
[Display(Name = "添加日期")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]//日期格式化
public DateTime AddTime { get; set; }
/// <summary>
/// 客户对应的订单信息
/// </summary>
public virtual IList<Order> Order { get; set; }
} // [NotMapped]//表不映射到数据库
[Table("Tb_Order")]//表名
public class Order
{
/// <summary>
/// ID
/// </summary>
[Key]
[Column("ID", Order = , TypeName = "int")]
[Required()]
[Display(Name = "ID")]
public int ID { get; set; }
/// <summary>
/// 名称
/// </summary>
[Column("Name", TypeName = "nvarchar")]
[StringLength(, ErrorMessage = "{0}长度不能超过50个字符")]
[Display(Name = "名称")]
public string Name { get; set; }
/// <summary>
/// 价格
/// </summary>
[Column("Price")]
[Display(Name = "价格")]
public decimal? Price { get; set; }
/// <summary>
/// 备注
/// </summary>
[StringLength()]//长度约束
[Column("Remark", TypeName = "nvarchar")]//我加了WYY看效果
[Display(Name = "备注")]
public string Remark { get; set; }
/// <summary>
/// 客户ID
/// </summary>
[ForeignKey("Customer")]//外键
public int CustomerID { get; set; }
/// <summary>
/// 订单对应的客户信息
/// </summary>
[ForeignKey("CustomerID")]//外键
public virtual Customer Customer { get; set; } /// <summary>
/// 不映射字段
/// </summary>
[NotMapped]//不映射到数据库
public string XXX { get; set; }
}

4、三者约定之 Fluent API
这个就要在派生类重写OnModelCreating了 少一点 的表还可以在里面设置各个字段多了还是映射Map在模型表里面写,在OnModelCreatiing注册模型类就可以了;
public DbSet<Customer> Customer { get; set; }
public DbSet<Order> Order { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//TODO 配置映射
modelBuilder.Entity<Customer>().ToTable("CSTo");//数据库表名
modelBuilder.Entity<Customer>().HasKey(x => x.Zj);//主键
modelBuilder.Entity<Customer>().Property(x=>x.AddTime).HasColumnType("DATETIME2");//时间
modelBuilder.Entity<Customer>().Property(x=>x.Age).IsOptional();//为null
//HasColumnType("DATETIME2(7)")这种写是错的
modelBuilder.Entity<Customer>().Property(x => x.Name).IsRequired().HasColumnType("varchar").HasMaxLength();//不为空,类型,长度
//默认情况下不会生成复数的表 如Orders
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new OrderMap());//注册
base.OnModelCreating(modelBuilder);
}
// [NotMapped]//表不映射到数据库
[Table("Tb_Order")]//表名
public class Order
{
///字段
}
public class OrderMap : EntityTypeConfiguration<Order>
{
public OrderMap()
{
//对应数据库表名
this.ToTable("ORd");
//一个订单必须对应有一个客户,客户一对多(订单) 用户表里面的 CustomerID
this.HasRequired(p => p.Customer).WithMany(p => p.Order).HasForeignKey(p => p.CustomerID);
this.HasKey(k => k.ID);//主键
this.Property(p => p.Name).HasColumnType("VARCHAR").HasMaxLength().IsRequired();//Name字段属性(varchar,长度50,不为null)
this.Property(p => p.Remark).HasColumnType("VARCHAR").HasMaxLength().IsOptional();//Remark(varchar,长度5000,null)
this.Property(p => p.Price).HasColumnName("pp");//列名
}
}

C#的数值类型对应数据库如下
●C#中的 int类型默认映射后对应数据库中的int类型。
● C#中的double类型默认映射后对应数据库中的float类型
●C#中的float类型默认映射后对应数据库中的real类型。
●C#中 的decimal类型默认映射后对应数据库中的decimal(18,2)类型
●C#中 的Int64类型默认映射后对应数据库中的bigint类型。
一般都是用Data Anntations 跟默认的约定好久没用记录一下 用到又来拿 Fluent API也是好久没复习了 哈哈 有时间在看看书
EF 学习系列二 数据库表的创建和表关系配置(Fluent API、Data Annotations、约定)的更多相关文章
- windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)
windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) windows下 ...
- MyBatis学习系列二——增删改查
目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring 数据库的经典操作:增删改查. 在这一章我们主要说明一下简单的查询和增删改, ...
- scrapy爬虫学习系列二:scrapy简单爬虫样例学习
系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备: http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...
- [转]ASP.NET MVC学习系列(二)-WebAPI请求 传参
[转]ASP.NET MVC学习系列(二)-WebAPI请求 传参 本文转自:http://www.cnblogs.com/babycool/p/3922738.html ASP.NET MVC学习系 ...
- RabbitMQ学习系列二-C#代码发送消息
RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列 http://www.80iter.com/blog/1437455520862503 上一篇已经讲了Rabbi ...
- 图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)
项目链接:https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1 欢迎fork欢迎三连!文章篇幅有限, ...
- [Unity3D插件]2dtoolkit系列二 动画精灵的创建以及背景图的无限滚动
经过昨天2dtoolkit系列教程一的推出,感觉对新手还有有一定的启发作用,引导学习使用unity 2dToolKit插件的使用过程,今天继续系列二——动画精灵的创建,以及背景图的无限循环滚动,在群里 ...
- .net reactor 学习系列(二)---.net reactor界面各功能说明
原文:.net reactor 学习系列(二)---.net reactor界面各功能说明 安装了.net reactor之后,可以在安装目录下找到帮助文档REACTOR_HELP.c ...
- Spring5.0源码学习系列之浅谈BeanFactory创建
Spring5.0源码学习系列之浅谈BeanFactory创建过程 系列文章目录 提示:Spring源码学习专栏链接 @ 目录 系列文章目录 博客前言介绍 一.获取BeanFactory主流程 二.r ...
随机推荐
- HZOJ visit
对于前30%的数据,可以考虑dp,f[i][j][k]表示时间为i,在i,j位置的方案数,枚举转移即可.要注意的是可以走到矩阵外. 对于另外30%数据,考虑推一下式子,设向右走y步,左z,上s,下x. ...
- day7_python之面向对象item系列(__getitem__,__setitem__,__delitem__)
class Foo: def __getitem__(self, item): print('=====>get') return self.__dict__[item] def __setit ...
- element表格多选实现单选
9.element多选表格实现单选 userChoose(selection, row) { console.log(selection,'selection') console.log(row,'r ...
- hdu 5745 La Vie en rose(2016多校第二场)
La Vie en rose Time Limit: 14000/7000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)T ...
- oracle不明确的索引等级
当ORACLE无法判断索引的等级高低差别,优化器将只使用一个索引,它就是在WHERE子句中被列在最前面的. 举例: DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. SELECT ...
- springboot2.04与activiti 6.0集成
本文就不对activiti做解释,下面直接看项目集成 以下顺序方面根据我的理解来,可以先从第二章看,再看第一张与第三章 增加activiti表的API,备注用. 目录 一.springboot2.X集 ...
- jq杂项方法/工具方法----trim() html() val() text() attr()
https://www.cnblogs.com/sandraryan/ $.trim() 函数用于去除字符串两端的空白字符.在中间的时候不会去掉. var str = ' 去除字符串左右两端的空格,换 ...
- Vue 设置style属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- mysql导入文件出现Data truncated for column 'xxx' at row 1的原因
mysql导入文件的时候很容易出现"Data truncated for column 'xxx' at row x",其中字符串里的xxx和x是指具体的列和行数. 有时候,这是因 ...
- P1077 旅行
题目描述 你要进行一个行程为7000KM的旅行,现在沿途有些汽车旅馆,为了安全起见,每天晚上都不开车,住在汽车旅馆,你手里现在已经有一个旅馆列表,用离起点的距离来标识,如下: 0, 990, 1010 ...