EF是一个ORM工具,映射永远是最核心的部分。所以接下来详细介绍Code First模式下EF的映射配置。

通过Code First来实现映射模型有两种方式Data Annotation和Fluent API。

Data Annotation需要在实体类的属性上以Attribute的方式表示主键、外键等映射信息。这种方式不符合解耦合的要求所以一般不建议使用。

第二种方式就是要重点介绍的Fluent API。Fluent API的配置方式将实体类与映射配置进行解耦合,有利于项目的扩展和维护。

Fluent API方式中的核心对象是DbModelBuilder。

在重写的DbContext的OnModelCreating方法中,我们可以这样配置一个实体的映射:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Product>().HasKey(t => t.Id);
    base.OnModelCreating(modelBuilder);
    }

1.配置类对应于数据库中的表名

  1. modelBuilder.Entity<Product>().ToTable("Product","dbo");

 配置类对应于数据库中的表名,并指定表的所有者:

  1. 如果不指定表的所有者可以这样写
  1. modelBuilder.Entity<Product>().ToTable("Product");

2.重新指定类属性与列名之间的映射关系

  1. 在默认约定的情况下,Entity Framework Code First创建的列名与类的属性名相同,可以根据需要进行重新指定类属性与列名之间的映射关系。
  1. modelBuilder.Entity<Product>().Property(t => t.ProductID).HasColumnName("ProductId");
  1. ProductID改为ProductId.
  1. modelBuilder.Entity<Product>().Property(t => t.ProductName).IsRequired()
  2. .HasColumnName("ProductName")
  3.      .HasMaxLength(100);

ProductName是必须的,映射到数据库的名字为ProductName,长度为100.

3.为属性指定对应的SQL SERVER数据类型

  1. 在默认情况下,int类型的属性生成的列名对应SQL SERVERint类型;而String类型的属性则对应SQL SERVER列的NVARCHAR类型。若类的字符串类型属性未设置MaxLength,则生成对应的列类型为NVARCHAR(MAX)。
    为属性指定对应的SQL SERVER数据类型:
  1. modelBuilder.Entity<Product>().Property(t => t.UnitPrice)
  2. .HasColumnName("UnitPrice")
  3. .HasColumnType("MONEY");

4.对主键的进行重写

  1. Entity Framework Code First的默认主键约束:属性名为[ID]或[类名 + ID]。如在Product类中,Entity Framework Code First会根据默认约定将类中名称为IDProductID的属性设置为主键。Entity Framework Code First主键的默认约定也一样可以进行重写,重新根据需要进行设置。
  1. modelBuilder.Entity<Product>().HasKey(t => t.ProductID);
  1. 若一个表有多个主键时:
  1. modelBuilder.Entity<Product>().HasKey(t => new { t.KeyID, t.CandidateID });
  1. Entity Framework Code First对于int类型的主键,会自动的设置其为自动增长列。但有时我们确实不需是自动增长的,可以通过以下方式进行取消自动增长。
  1. modelBuilder.Entity<Product>().HasKey(t => t.ProductID)
  2. .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//取消自动增长
  3. modelBuilder.Entity<Category>().HasKey(t => t.ProductID)
  4. .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);//将ProductId设置为自动增长

 5.设置类型的的精度 

  1. Product类中,UnitPrice表示单价,对于价格类的字段,我们通常会希望其保留2为小数。这时可以使用Fluent API进行设置
  1. modelBuilder.Entity<Product>().Property(t => t.UnitPrice)
  2. .HasColumnName("UnitPrice")
  3. .HasPrecision(18, 2);

  

6、非数据库字段属性

  在类中,如果有一些属性不需要映射到对应生成的数据表中,可以通过以下方式设置。

  1. modelBuilder.Entity<Product>().Ignore(t => t.Remark);

  

7. Fluent API配置Configuration映射类

  在使用Fluent API进行Entity Framework Code First数据库映射时,除了以上的在重写OnModelCreating方法中直接对Entity进行配置之外,也可以对Configurations进行配置。这时可以先写一个单独的类,将数据表的全部映射要求都写在构造函数中。

类要继承

  1. EntityTypeConfiguration<T>,然后再构造函数中添加映射,最后
  1. modelBuilder.Configurations.Add(new T());
  1.  

8.拓展

  1. 关联 1-1关联
  2.  
  3. Fluent API设置实体类生成的表引用与被引用通过WithRequiredPrincipalWithRequiredDependentWithOptionalPrincipalWithOptionalDependent来设置,使用Principal属性的实体类将被另外的实体类生成的表引用,使用Dependent属性的实体类将引用另外的实体类。
    这里说明一下
    WithRequiredDependent WithOptional(i => i.Product)是等价的;
//第一组(两条效果完全相同)
       

  1. HasRequired(p => p.WarrantyCard).WithRequiredDependent(i => i.Product);
  2. HasRequired(p => p.WarrantyCard).WithOptional(i => i.Product);

  

外键指定在Product表的Id列上,Product的主键Id不作为标识列。
WithRequiredPrincipal 和 WithRequired是等价的
//第二组(两条效果完全相同)
      

  1. HasRequired(p => p.WarrantyCard).WithRequiredPrincipal(i => i.Product);
  2. HasOptional(p => p.WarrantyCard).WithRequired(i => i.Product);

  

外键添加到WarrantyCard表的主键ProductId上,而且这个键也不做标识列使用了。
对于当前场景这两组配置应该选择那一组呢。对于产品和保修卡,肯定是先有产品后有保修卡,保修卡应该依赖于产品而存在。所以第二组配置把外键设置到WarrantyCard的主键更为合适,让WarrantyCard依赖Product符合当前场景。即Product作为Principal而WarrantyCard作为Dependent,其实这么多代码也无非就是明确两个关联对象Principal和Dependent的地位而已。
单向1 - *关联(可为空)

这里新登场角色是和发票发票有自己的编号,有些产品有发票,有些产品没有发票。我们希望通过产品找到发票而又不需要由发票关联到产品。

  1. public class Invoice
  2. {
  3. public int Id { get; set; }
  4. public string InvoiceNo { get; set; }
  5. public DateTime CreateDate { get; set; }
  6. }

产品类新增的属性如下:

  1. public virtual Invoice Invoice { get; set; }
  2. public int? InvoiceId { get; set; }
 

可以使用如下代码创建Product到Invoice的关联

  

  1. public class ProductMap : EntityTypeConfiguration<Product>
  2. {
  3. public ProductMap()
  4. {
  5. ToTable("Product");
  6. HasKey(p => p.Id);
  7. HasOptional(p => p.Invoice).WithMany().HasForeignKey(p => p.InvoiceId);
  8. }
  9. }

  

HasOptional表示一个产品可能会有发票,WithMany的参数为空表示我们不需要由发票关联到产品,HasForeignKey用来指定Product表中的外键列。

还可以通过WillCascadeOnDelete()配置是否级联删除,这个大家都知道,就不多说了。

运行迁移后,数据库生成的Product表外键可为空(注意实体类中表示外键的属性一定要为Nullable类型,不然迁移代码不能生成)。

单向1 - *关联(不可为空)

为了演示这个关联,请出一个新对象合格证合格证有自己的编号,而且一个产品是必须有合格证。

  1. public class Certification
  2.  
  3. {
  4. public int Id { get; set; }
  5. public string Inspector { get; set; }
  6. }

  

我们给Product添加关联合格证的属性:
  1. public virtual Certification Certification { get; set; }
  2. public int CertificationId { get; set; }

  

配置Product到Certification映射的代码与之前的类似,就是把HasOptional换成了HasRequired:
HasRequired(p => p.Certification).WithMany().HasForeignKey(p=>p.CertificationId);
生成的迁移代码,外键列不能为空。创建对象时Product必须和Certification一起创建。生成的查询语句除了把LEFT OUTER JOIN换成INNER JOIN外其他都一样,不再赘述。
双向1 - *关联
这是比较常见的场景,如一个产品可以对应多张照片,每张照片关联一个产品。先来看看新增的照片类
  1. public class ProductPhoto
  2. {
  3. public int Id { get; set; }
  4. public string FileName { get; set; }
  5. public float FileSize { get; set; }
  6. public virtual Product Product { get; set; }
  7. public int ProductId { get; set; }
  8. }
给Product增加ProductPhoto集合:
  1. public virtual ICollection<ProductPhoto> Photos { get; set; }

  

然后是映射配置:
  1. public class ProductMap : EntityTypeConfiguration<Product>
  2. {
  3. public ProductMap()
  4. {
  5. ToTable("Product");
  6. HasKey(p => p.Id);
  7. HasMany(p => p.Photos).WithRequired(pp => pp.Product).HasForeignKey(pp => pp.ProductId);
  8. }
  9. }
代码很容易理解,HasMany表示Product中有多个ProductPhoto,WithRequired表示ProductPhoto一定会关联到一个Product。
我们来看另一种等价的写法(在ProductPhoto中配置关联):
  1. public class ProductPhotoMap : EntityTypeConfiguration<ProductPhoto>
  2. {
  3. public ProductPhotoMap()
  4. {
  5. ToTable("ProductPhoto");
  6. HasKey(pp => pp.Id);
  7. HasRequired(pp => pp.Product).WithMany(p => p.Photos).HasForeignKey(pp => pp.ProductId);
  8. }
  9. }
有没有感觉和之前单向1 - *的配置很像?其实就是WithMany多了参数而已。随着例子越来越多,大家应该对这几个配置理解的越来越深了。
* - *关联
一个产品可以有多个标签,一个标签也可对应多个产品:
  1. public class Tag
  2. {
  3. public int Id { get; set; }
  4. public string Text { get; set; }
  5. public virtual ICollection<Product> Products { get; set; }
  6. }

  

给Product增加标签集合:
  1. public virtual ICollection<Tag> Tags { get; set; }
  2. public class ProductMap : EntityTypeConfiguration<Product>
  3. {
  4. public ProductMap()
  5. {
  6. ToTable("Product");
  7. HasKey(p => p.Id);
  8. HasMany(p => p.Tags).WithMany(t => t.Products).Map(m => m.ToTable("Product_Tag_Mapping"));
  9. }
  10. }
 
  1.  

Entity Framework映射的总结的更多相关文章

  1. Entity Framework 映射问题

    今天在数据库(mysql)新增了一个字段,但是一直以为添加字段,然后在实体模型中选择 一直是以为选择"添加",就导致有问题,原因就不说,有点蠢,人家都已经存在,还加上去干嘛,我要的 ...

  2. 第三篇:Entity Framework CodeFirst & Model 映射 续篇 EntityFramework Power Tools 工具使用

    上一篇 第二篇:Entity Framework CodeFirst & Model 映射 主要介绍以Fluent API来实作EntityFramework CodeFirst,得到了大家一 ...

  3. 《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-8 Table per Type Inheritance 建模 问题 你有这样一 ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-10 Table per Hierarchy Inheritance 建模 问题 ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (35) ------ 第六章 继承与建模高级应用之TPH继承映射中使用复合条件

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-11  TPH继承映射中使用复合条件 问题 你想使用TPH为一张表建模,建模中使 ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (36) ------ 第六章 继承与建模高级应用之TPC继承映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-12  TPC继承映射建模 问题 你有两张或多张架构和数据类似的表,你想使用TP ...

  8. Entity Framework Code First属性映射约定

    Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...

  9. Entity Framework Code First关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

随机推荐

  1. 对Git的一些理解

    使用Git都快2年了,能够说熟练使用git,遇到不会的也可以自己查询git帮助手册.平时可以根据shell的管道命令,组合一些命令比如git show commitID | grep “diff”来看 ...

  2. tastypie Django REST framework API [Hello JSON]

    tastypie is a good thing. Haven't test it thoroughly. Gonna need some provement. Now I will introduc ...

  3. Ordering是Guava

    Guava学习笔记:Ordering犀利的比较器   Ordering是Guava类库提供的一个犀利强大的比较器工具,Guava的Ordering和JDK Comparator相比功能更强.它非常容易 ...

  4. java动态加载配置文件

    最近项目中需要做定时任务,即定时数据库的备份.定时时间用户可以在界面中配置,要求配置修改好立即生效. 想不到什么好办法.下面是一种实现思路 把用户配置的时间存到properties配置文件中,定时任务 ...

  5. 【Unity 3D】教程(1)建立场景

    1.新建一个地形 在菜单中选择Terrain,新建一个地形 接下来在右边的“编辑高度”中,用笔刷绘出地形高度,如图: 2.地形纹理 接下来我们给地形贴上纹理,纹理资源我们使用unity自带的, 在Pr ...

  6. [置顶] ROS探索总结(十一)——机器视觉

    机器视觉在计算机时代已经越来越流行,摄像头价格越来越低廉,部分集成深度传感器的混合型传感器也逐渐在研究领域普及,例如微软推出的Kinect,而且与之配套的软件功能十分强大,为开发带来了极大的便利.RO ...

  7. Linux负载均衡软件LVS之二(安装篇)

    一.  安装LVS软件 1.安装前准备工作操作系统:统一采用Centos4.4版本.地址规划,如表1所示:表1 更详细的信息如图2所示: 图2中的VIP指的是虚拟IP地址,还可以叫做LVS集群的服务I ...

  8. java基础IO流综合加习题

    IO流初学者在学习时都有一点迷糊,今天我们就讲讲IO流,希望通过讲解可以帮助大家 IO流分为字节流,字符流,缓冲流.我们只要记住这三个就可以了. 1*字节流有:字节输入流(FileInputStrea ...

  9. java框架之struts2简介

    一.Struts2简介 1.Struts2概述                    Struts2是Apache发行的MVC开源框架.注意:它只是表现层(MVC)框架. M:model-----数据 ...

  10. ESP32学习笔记(一) 环境搭建与下载

    ESP32学习笔记(一) 环境搭建与下载 作者:Nevel 博客:nevel.cnblogs.com 转载请保留出处 前几天刚入手了ESP32模块,趁着放假有时间,我们先把ESP32的编译环境搭建好 ...