EF Code First 学习笔记:表映射
多个实体映射到一张表
Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则:
- 实体必须是一对一关系
- 实体必须共享一个公共键
观察下面两个实体:

public class Person
{
[Key]
public int PersonId { get; set; }
public int SocialSecurityNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public PersonPhoto Photo { get; set; }
} public class PersonPhoto
{
[Key, ForeignKey("PhotoOf")]
public int PersonId { get; set; }
public byte[] Photo { get; set; }
public string Caption { get; set; }
public Person PhotoOf { get; set; }
}

它们之间是一对一的关系,并且主键数据类型相同,所以我们可以将它们映射到同数据库的同一个表中,只需指定表名即可:
[Table("People")]
public class Person
[Table("People")]
public class PersonPhoto
PS:我按照上面的模型映射,但生成数据库的时候会报错:
实体类型“PersonPhoto”和“Person”无法共享表“People”,因为它们不在同一类型层次结构中,或者它们之间的匹配的主键没有有效的一对一外键关系。
然后我又把模型改了一下:

[Table("People")]
public class Person
{
[Key, ForeignKey("Photo")]
public int PersonId { get; set; }
public int SocialSecurityNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public PersonPhoto Photo { get; set; }
}
[Table("People")]
public class PersonPhoto
{
[Key, ForeignKey("PhotoOf")]
public int PersonId { get; set; }
public byte[] Photo { get; set; }
public string Caption { get; set; }
public Person PhotoOf { get; set; }
}

映射可以成功,成功映射后的表结构如图:

但是在插入数据的时候Person类中的Photo属性不能为空,否则会报错:
遇到了无效数据。缺少必要的关系。请检查 StateEntries 以确定违反约束的源。
PersonPhoto ph = new PersonPhoto() { Caption = "个人照片",Photo=new byte[8]};
//可以插入成功
Person p1 = new Person() { FirstName = "Jhon", LastName = "Micheal",SocialSecurityNumber=123,Photo=ph};
//没有给Photo赋值,插入失败
Person p2 = new Person() { FirstName = "Jhon", LastName = "Micheal",SocialSecurityNumber=123};
将一个实体映射到多张表
现在我们反转一下,将一个实体映射到多张表,这可以用Fluent API的Map方法来实现,不能使用使用Data Annotations ,因为Data Annotations 没有属性子集的概念。
我们就将People表映射到数据库中A,B两表

public class PersonInfo
{
[Key]
public int PersonId { get; set; }
public int SocialSecurityNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public byte[] Photo { get; set; }
public string Caption { get; set; }
}


modelBuilder.Entity<PersonInfo>().Map(m =>
{
m.ToTable("A");
m.Properties(p => p.FirstName);
m.Properties(p => p.LastName);
m.Properties(p => p.RowVersion);
m.Properties(p => p.SocialSecurityNumber);
}).Map(m =>
{
m.ToTable("B");
m.Properties(p => p.Photo);
m.Properties(p => p.Caption);
});

生成的表结构如图:

可以看到,Code First自动的为这两张表创建了主键和外键。在生成的表中,只有主表(A表)的主键是自增长的。
注意:用Map映射的时候务必不要跳过任何属性!不然Code First还会自动的创建第三张表,保存那些你遗漏的属性。

上面的PersonInfo11就是Code First自动创建的第三张表,因为我Map的时候遗漏了SocialSecurityNumber属性。
继承类的映射
TPH(Table Per Hierarchy)
TPH:基类和派生类都映射到同一张表中,通过使用鉴别列来识别是否为子类型。这是Code First默认规则使用的表映射方法。

public class Lodging
{
public int LodgingId { get; set; }
[Required]
[MaxLength(200)]
[MinLength(10)]
public string Name { get; set; }
public string Owner { get; set; }
public decimal MilesFromNearestAirport { get; set; }
public int DestinationId { get; set; }
}
public class Resort : Lodging
{
public string Entertainment { get; set; }
public string Activities { get; set; }
}

生成的数据结构如图:

所以的属性都映射到同一张表中,包括派生类中的Entertainment,Activities,而且还多了一列:Discriminator。EF正是通过这一列来识别数据来源。 我们可以插入数据测试一下:

var lodging = new Lodging
{
Name = "Rainy Day Motel",
}; var resort = new Resort
{
Name = "Top Notch Resort and Spa",
MilesFromNearestAirport = 30,
Activities = "Spa, Hiking, Skiing, Ballooning",
};
using (var context = new BreakAwayContext())
{
context.Lodgings.Add(lodging);
context.Lodgings.Add(resort);
context.SaveChanges();
}


可以看到EF通过Discriminator来区分Lodging与Resort。
使用Fluent API定制TPH区分符字段
如果你觉得默认的鉴别列(discriminator)列名不够直观的话,我们可以通过Fluent API来配置discriminator列的类型和命名(Data Annotations 没有标记可用于定制TPH)。

modelBuilder.Entity<Lodging>().Map(m =>
{
m.ToTable("Lodgings");
m.Requires("LodgingType").HasValue("Standard");
}).Map<Resort>(m =>
{
m.Requires("LodgingType").HasValue("Resort");
});


Requires的参数即是你要的列名,HasValue用来指定鉴别列中的值。
如果基类只有一个派生类,我们也可以将鉴别列的数据类型设置为bool值:

modelBuilder.Entity<Lodging>().Map(m =>
{
m.ToTable("Lodging");
m.Requires("IsResort").HasValue(false);
})
.Map<Resort>(m =>
{
m.Requires("IsResort").HasValue(true);
});

TPT(Table Per Type)
TPH将所有层次的类都放在了一个表里,而TPT在一个单独的表中储存来自基类的属性,在派生类定义的附加属性储存在另一个表里,并使用外键与主表相连接。
我们显示的指定派生类生成的表名即可:
[Table("Resorts")]
public class Resort : Lodging
{
public string Entertainment { get; set; }
public string Activities { get; set; }
}

我们可以看到生成了两张表,模型中的派生类Resort映射的表中只包括它自已的两个属性:Entertainment、Activities,基类映射的表中也只包含它自己的属性。并且Resorts中LodgingId即是主键也作为外键关联到表Lodgings.


TPC(Table Per Concrete Type)
TPC类似TPT,基类与派生类都映射在不同的表中,不同的是派生类中还包括了基类的字段。TPC只能用Fluent API来配置。

modelBuilder.Entity<Lodging>().Map(m =>
{
m.ToTable("Lodgings");
}).Map<Resort>(m =>
{
m.ToTable("Resorts");
m.MapInheritedProperties();
});


生成的数据库中派生类Resort映射的表中Resorts也包括了Lodging中的字段。
PS:不知道为什么,发现TPC模式,EF默认情况不会为表生成字增长。
EF Code First 学习笔记:表映射的更多相关文章
- EF Code First学习笔记
EF Code First学习笔记 初识Code First EF Code First 学习笔记:约定配置 Entity Framework 复杂类型 Entity Framework 数据生成选项 ...
- EF Code First 学习笔记:表映射 多个Entity到一张表和一个Entity到多张表
多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Per ...
- EF Code First 学习笔记:表映射(转)
多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Per ...
- EF Code First 学习笔记:约定配置 Data Annotations+Fluent API
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...
- [转载]EF Code First 学习笔记:约定配置
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...
- EF Code First 学习笔记:约定配置
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...
- 【转】EF Code First 学习笔记:约定配置
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...
- EF Code First 学习笔记:约定配置(转)
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一 ...
- EF Code First学习笔记 初识Code First
Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来 ...
随机推荐
- 【socket】TCP 和 UDP 在socket编程中的区别
一.TCP与UDP的区别 基于连接与无连接 对系统资源的要求(TCP较多,UDP少) UDP程序结构较简单 流模式与数据报模式 TCP保证数据正确性,UDP可能丢包 TCP保证数据顺序,UD ...
- 深度:ARC会导致的内存泄露
iOS提供了ARC功能,很大程度上简化了内存管理的代码. 但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露. 下面列举两种内存泄露的情况. 1,循环参照 A有个属性参照B,B有个属性 ...
- easy ui datagrid 增,删,改,查等基本操作
如下图: ①列表信息图 ②添加信息图 ③修改信息图 html代码: <%@ Page Title="" Language="C#" MasterPageF ...
- iphone编程,使用代码结束程序
(ios 4环境) 1.使用代码可以结束自己只有一种方式,而且apple不建议这样用,就是调用exit()函数.在2.0以前uiapplication类中有一个方法可以调用,但是现在已经没有了,而且如 ...
- 如何把由js生成的内容水平居中?
一.问题来源 如右侧的微博关注组件,直接用div的align居中没效果,我就开始百度了.即在<div> <script>.....</script> </d ...
- 学点PYTHON基础的东东--数据结构,算法,设计模式---单向链表
看来看来,还是以下这个实现最优雅.. 其它的,要么NODE冗余,要么初始化丑陋... #!/usr/bin/env python # -*- coding: utf-8 -*- class Node: ...
- linux查看在线用户 who命令参数及用法
linux who 命令 详解 Linux最常用命令之一 功能说明:显示目前登入系统的用户信息. 语 法:who [-Himqsw][--help][--version][am i][记录文件] 补充 ...
- Android studio 下的 NDK 配置方法和注意事项
http://blog.csdn.net/u013598660/article/details/47341963
- [mock]12月11日
给出一个二叉搜索树的先序遍历,要求重新构造出这个二叉搜索树.一开始给出了一个递归的算法,指出复杂度有更优的解法.然后想出来一个O(n)的算法,就是使用一个栈,然后依次判断新的节点比原来的大还是小,然后 ...
- linux下使用yum安装mysql、tomcat、httpd
一.linux下使用yum安装mysql 1.安装 查看有没有安装过: yum list installed mysql* rpm -qa | grep m ...