多个实体映射到一张表

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 学习笔记:表映射的更多相关文章

  1. EF Code First学习笔记

    EF Code First学习笔记 初识Code First EF Code First 学习笔记:约定配置 Entity Framework 复杂类型 Entity Framework 数据生成选项 ...

  2. EF Code First 学习笔记:表映射 多个Entity到一张表和一个Entity到多张表

      多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Per ...

  3. EF Code First 学习笔记:表映射(转)

      多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Per ...

  4. EF Code First 学习笔记:约定配置 Data Annotations+Fluent API

    要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...

  5. [转载]EF Code First 学习笔记:约定配置

    要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...

  6. EF Code First 学习笔记:约定配置

    要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...

  7. 【转】EF Code First 学习笔记:约定配置

    要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...

  8. EF Code First 学习笔记:约定配置(转)

      要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一 ...

  9. EF Code First学习笔记 初识Code First

    Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来 ...

随机推荐

  1. Mapped Statements collection does not contain value for TaskMapper.selectByPrimaryKey

    Mapped Statements collection does not contain value for后面是什么类什么方法之类的: 错误原因有几种: 1.mapper.xml中没有加入name ...

  2. kindeditor-4.1.10 结合 Asp.Net MVC 添加图片功能

    KindEditor是一套开源的HTML可视化编辑器,现在我要结合Asp.Net MVC4 上传图片功能,做相应的配置和修改, 其实网上也有人写过类似的文章了,我写出来是以防以后使用的时候出现这样的问 ...

  3. Detect loop in a singly linked list

    去Twitter面试的被问到这个问题,当时只想到了用HashMap的办法,这种办法时间复杂度O(n),空间复杂度是O(n), 更好的办法是用 FastRunner / SlowRunner appro ...

  4. Spark的TorrentBroadcast:实现

    依据Spark 1.4版 序列化和反序列化 前边提到,TorrentBroadcast的关键就在于特殊的序列化和反序列化设置.1.1版的TorrentBroadcast实现了自己的readObject ...

  5. http://jingyan.baidu.com/article/e4511cf33479812b855eaf67.html

    http://jingyan.baidu.com/article/e4511cf33479812b855eaf67.html

  6. 孟岩的c++ 的学习方法,这何尝有不是做人做事的方法呢?

    “(孟岩)我主张,在具备基础之后,学习任何新东西,都要抓住主线,突出重点.对 于关键理论的学习,要集中精力,速战速决.而旁枝末节和非本质性的知识内容,完全可 以留给实践去零敲碎打. “原因是这样的,任 ...

  7. Servlet课程0426(十一)Servlet Cookie实现两周内不用重复登录

    Welcome.java //登录界面 package com.tsinghua; import javax.servlet.http.*; import java.io.*; import java ...

  8. vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装。的解决方法

    vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装.的解决方法_华英雄_新浪博客 http://blog.sina.com.cn/s/blog_5 ...

  9. 利用CCProxy管理小型企业的上网行为

    本实验以实例方式,从操作条件.背景.需求.以及具体要求的几个部分进行说明. 1. 操作条件: 装有Windows Server 2003系统,安装了代理服务程序的虚拟机一台 2. 背景: 为了提高员工 ...

  10. 最受欢迎的5个Android ORM框架

    在开发Android应用时,保存数据有这么几个方式, 一个是本地保存,一个是放在后台(提供API接口),还有一个是放在开放云服务上(如 SyncAdapter 会是一个不错的选择). 对于第一种方式, ...