博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
EF Code First 学习笔记:表映射
阅读量:6463 次
发布时间:2019-06-23

本文共 5991 字,大约阅读时间需要 19 分钟。

多个实体映射到一张表

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
().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来区分LodgingResort

使用Fluent API定制TPH区分符字段

如果你觉得默认的鉴别列(discriminator)列名不够直观的话,我们可以通过Fluent API来配置discriminator列的类型和命名(Data Annotations 没有标记可用于定制TPH)。

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

Requires的参数即是你要的列名,HasValue用来指定鉴别列中的值。

如果基类只有一个派生类,我们也可以将鉴别列的数据类型设置为bool值:

modelBuilder.Entity
().Map(m => { m.ToTable("Lodging"); m.Requires("IsResort").HasValue(false); }) .Map
(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
().Map(m => { m.ToTable("Lodgings"); }).Map
(m => { m.ToTable("Resorts"); m.MapInheritedProperties(); });

生成的数据库中派生类Resort映射的表中Resorts也包括了Lodging中的字段。

PS:不知道为什么,发现TPC模式,EF默认情况不会为表生成字增长。

 

转载地址:http://kwhzo.baihongyu.com/

你可能感兴趣的文章
java 合成模式_《JAVA与模式》之合成模式
查看>>
java snippet_snippet,让你编码效率翻倍
查看>>
java cookie 覆盖_Java 会话技术 之cookie
查看>>
java 概率分布_将概率分布拟合到数据并找到它的累积分布函数
查看>>
java this()函数_java中this关键字怎么使用?
查看>>
java split limit_java split(String regex, int limit) 的使用
查看>>
java校园导航_校园导航的设计与实现(SSH,MySQL)(含录像)
查看>>
archlinux java环境_Arch Linux快速配置Java环境变量
查看>>
jfreechart java_java jfreechart
查看>>
python键盘输入转换为列表_Python键盘输入转换为列表的实例
查看>>
java字符是否数字_Java如何检查字符是否代表数字?
查看>>
java常用到的知识总结_java开发常用知识总结
查看>>
jsp中java向js传值_jsp想js,action传值
查看>>
opatch无法定位java_Java (1.6) could not be located. OPatch cannot proceed! — oracle-tech
查看>>
魏永红java课后答案_基于J2ME技术的手机信息查询系统的设计与实现
查看>>
java有交互式_Java 9 尝鲜之交互式编程环境
查看>>
galera mysql docker话_bug排查之空想导致的祸之MariaDB Galera Cluster的docker镜像构建中的遇到的坑爹事...
查看>>
php i=i 怎么实现,ThinkPHP的I方法使用详解
查看>>
java 对输入的字符串处理方法,JavaScript中对字符串操作方法总结--Java免费学习网...
查看>>
java 猫 游戏,crazycat 围住神经猫-小游戏-Java源码 联合开发网 - pudn.com
查看>>