博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Entity Framwork CodeFirst 学习笔记五:数据库映射的默认配置和设置
阅读量:6451 次
发布时间:2019-06-23

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

数据库的映射指的就是对数据库进行配置,包括生成表名,架构名,列名。这些内容前面的笔记已经涉及到了,还包括的复杂类型的设置,这里就不在赘述。

本次主要学习和掌握如何将单个类映射到多个表中,多个类如何映射到一个通用表中和各种类继承架构的配置。

让多个实体映射到同一个表:AKA表切分

通常一个数据库表中虽然有很多列,但在很多场景只需要使用其中的一部分,其他的只是一些附加的数据。当我们映射一个实体到这样的表以后,你会发现要浪费资源来处理一些无用的数据。表切分技术可以解决这个问题,这种技术允许在一个单独表中访问多个实体。

为了将多个实体映射到一个通用的表中,实体必须遵循如下规则:

  • 实体必须是一对一关系
  • 实体必须共享一个通用键

看一下书中的例子:

[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; }                public PersonPhoto Photo { get; set; }    }        [Table("People")]    public class PersonPhoto    {        //共享通用键        [Key, ForeignKey("PhotoOf")]        public int PersonId { get; set; }        [Column(TypeName="image")]        public byte[] Photo { get; set; }        public string Caption { get; set; }        public Person PhotoOf { get; set; }    }

 两类的确满足我们上面的关系,我们在程序运行的时候插入一条数据:

static void Main(string[] args) {    Database.SetInitializer(new DropCreateDatabaseIfModelChanges
()); InsertPerson(); } private static void InsertPerson() { PersonPhoto ph = new PersonPhoto() { Caption = "个人照片", Photo = new byte[8] }; //可以插入成功 Person p1 = new Person() { FirstName = "Jhon", LastName = "Micheal", SocialSecurityNumber = 123, Photo = ph }; using (var context = new LxContext()) { context.Persons.Add(p1); context.SaveChanges(); } }

 数据库生成后如下图:

注意:当我们这样做的时候,当我们插入数据的时候,两个类必须都拥有数据。否则会产生“遇到了无效数据。缺少必要的关系” 的异常!

 将一个单独的实体映射到多个表

翻转一下上面的例子,我们可以把 一个单独的实体映射的到多个表中,这种称之为实体分割。实现这个功能 不能使用Data Annotations ,因为Data Annotations 没有子属性的概念。

我们再增加一个类:

//将PersonInfo 分割成两个表    public class PersonInfo    {        [Key]        public int PersonId { get; set; }        public int SocialSecurityNumber { get; set; }        public string FirstName { get; set; }        public string LastName { get; set; }        public byte[] Photo { get; set; }        public string Caption { get; set; }    }

 然后用FluntAPI进行配置:

protected override void OnModelCreating(DbModelBuilder modelBuilder){   modelBuilder.Entity
().Map(m => { m.ToTable("t_PersonInfo"); m.Properties(p => p.FirstName); m.Properties(p => p.LastName); m.Properties(p => p.SocialSecurityNumber); }).Map(m => { m.ToTable("t_PersonPhoto"); m.Properties(p => p.Photo); m.Properties(p => p.Caption); });}

  结果如下图:

我们发现PersonInfo这个类的确被我们拆分成了两个表,t_PersonInfo 和 t_PersonPhoto,而且还进行了外键关联。

但这里有几个需要注意的地方:

1、虽然生成了两表,但是只有t_PersonInfo 的主键是自增的,t_PersonPhoto 的主键 PersonId 是非自增的,并且是外键关联到了 t_PersonInfo的主键
2、虽然建立了外键但是不会建立级联删除。
3、在使用FluntAPI 进行实体分割的时候,务必不要跳过任何属性。否则Code First还会自动的创建第三张表,保存那些你遗漏的属性。

虽然没有建立级联删除,但是 EF框架知道,如果对t_PersonInfo 操作,它必须建立一个跨越两个表的执行命令,我们来实验一下:

增加一个Person

var p2 = new PersonInfo{    FirstName = "Monroe",    LastName = "Marilyn",    SocialSecurityNumber = 456};using (var context = new LxContext()){    context.PersonInfos.Add(p2);    context.SaveChanges();}

我们利用Sql Profiler监视,会发现数据库执行了两条SQL分别是:

exec sp_executesql N'insert [dbo].[t_PersonInfo]([SocialSecurityNumber], [FirstName], [LastName])values (@0, @1, @2)select [PersonId]from [dbo].[t_PersonInfo]where @@ROWCOUNT > 0 and [PersonId] = scope_identity()',N'@0 int,@1 nvarchar(max) ,@2 nvarchar(max) ',@0=456,@1=N'Monroe',@2=N'Marilyn'exec sp_executesql N'insert [dbo].[t_PersonPhoto]([PersonId], [Photo], [Caption])values (@0, null, null)',N'@0 int',@0=1

 修改一个Person:

using (var context = new LxContext()){    var PersonList = context.PersonInfos.ToList();    var p = PersonList[0];    p.Caption = "my photo";    p.Photo = new byte[8];    context.SaveChanges();}

  监视执行的SQL 为:

--查询 SELECT [Extent1].[PersonId] AS [PersonId], [Extent2].[SocialSecurityNumber] AS [SocialSecurityNumber], [Extent2].[FirstName] AS [FirstName], [Extent2].[LastName] AS [LastName], [Extent1].[Photo] AS [Photo], [Extent1].[Caption] AS [Caption]FROM  [dbo].[t_PersonPhoto] AS [Extent1]INNER JOIN [dbo].[t_PersonInfo] AS [Extent2] ON [Extent1].[PersonId] = [Extent2].[PersonId]--修改exec sp_executesql N'update [dbo].[t_PersonPhoto]set [Photo] = @0, [Caption] = @1where ([PersonId] = @2)',N'@0 varbinary(max) ,@1 nvarchar(max) ,@2 int',@0=0x0000000000000000,@1=N'my photo',@2=1

 删除Person:

using (var context = new LxContext()){     var PersonList = context.PersonInfos.ToList();     var p = PersonList[0];     context.PersonInfos.Remove(p);     context.SaveChanges();}

 监视生成的SQL为:

--查询SELECT [Extent1].[PersonId] AS [PersonId], [Extent2].[SocialSecurityNumber] AS [SocialSecurityNumber], [Extent2].[FirstName] AS [FirstName], [Extent2].[LastName] AS [LastName], [Extent1].[Photo] AS [Photo], [Extent1].[Caption] AS [Caption]FROM  [dbo].[t_PersonPhoto] AS [Extent1]INNER JOIN [dbo].[t_PersonInfo] AS [Extent2] ON [Extent1].[PersonId] = [Extent2].[PersonId]--删除exec sp_executesql N'delete [dbo].[t_PersonPhoto]where ([PersonId] = @0)',N'@0 int',@0=1exec sp_executesql N'delete [dbo].[t_PersonInfo]where ([PersonId] = @0)',N'@0 int',@0=1

 通过这些操作,我们已经知道虽然没有生成级联删除或者修改,但是EF框架会知道如何执行,不用我们去手动管理。

类继承的映射与配置:

EF框架支持各种模型中的继承层次结构。无论你使用Code First,Model First还是Database First来定义模型都不用担心继承的类型问题,也不用考虑EF框架如何使用这些类型进行查询,跟踪变更和更新数据。

TPH(Table Per Hierarchy):

TPH:基类和派生类都映射到同一张表中,通过使用鉴别列来识别是否为子类型。这是Code First默认规则使用的表映射方法。

public class Pet{    public int PetId { get; set; }            public string PetName { get; set; } }public class Dog : Pet{    public int Age { get; set; }    public string Des { get; set; }}

 然后插入一些数据:

var pet = new Pet{     PetName = "兔宝宝",}; var dog = new Dog{   PetName="狗宝宝",   Age=1,   Des="1岁狗宝宝"}; using (var context = new LxContext()){    context.Pets.Add(pet);    context.Pets.Add(dog);    context.SaveChanges(); }

 运行程序会看到生成的表结构和插入的数据:

我们会发现,生成了一个表,其列名就是父类和子类属性的和,还有一鉴别列 Discriminator,看数据我们就能知道,鉴别列的数值是通过类名来进行区分的,Pet :为父类数据,Dog:为子类数据。

以上是默认的配置规则,当然也可以利用FluntAPI方式进行配置,修改鉴别列的列名和值,EF 会自动识别值的类型。

modelBuilder.Entity
().Map(m =>{ m.ToTable("t_Pet"); m.Requires("IsFather").HasValue(true);}).Map
(m =>{ m.Requires("IsFather").HasValue(false);});

TPT(Table Per Type):

TPH将所有层次的类都放在了一个表里,而TPT在一个单独的表中储存来自基类的属性,在派生类定义的附加属性储存在另一个表里,并使用外键与主表相连接。

我们只需要为派生类指定表名即可,可以使用Data Annotations 也可以使用Fluent API来完成这项工作。

DataAnnotation方式:

[Table("t_Pet")]public class Pet{   public int PetId { get; set; }           public string PetName { get; set; }}[Table("t_Dog")]public class Dog : Pet{   public int Age { get; set; }   public string Des { get; set; }}

 结果如下:

FluntAPI 方式: 

modelBuilder.Entity
().ToTable("t_Pet");modelBuilder.Entity
().ToTable("t_Dog");

 也可以更具体指定关系:

modelBuilder.Entity
().Map(m => { m.ToTable("t_Pet"); }).Map
(m => { m.ToTable("t_Dog"); });

 TPC(Table Per Concrete Type)

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

modelBuilder.Entity
().Map(m => { m.ToTable("t_Pet"); }).Map
(m => { m.ToTable("t_Dog"); m.MapInheritedProperties();});

生成表结构如下:

虽然生成了表,但是两个表的主键PetId 均不是自动增长,我们插入数据的时候,需要指定PetId:

var pet = new Pet{      PetId=1,      PetName = "兔宝宝", };//虽然生成了两个表,但是PetId 不能=1,否则出现重复键异常 var dog = new Dog {       PetId=2,       PetName="狗宝宝",       Age=1,       Des="1岁狗宝宝"}; using (var context = new LxContext()){       context.Pets.Add(pet);       context.Pets.Add(dog);       context.SaveChanges(); }

 结果如下:

注意:TPH 、TPT、TPC 三种种方式的 其他数据操作比如查询,删除,修改均没有试验,需要待考察!

转载于:https://www.cnblogs.com/lxblog/archive/2013/05/23/3094304.html

你可能感兴趣的文章
CocosCreator 小知识
查看>>
如何称为演讲高手
查看>>
PHP坑之积累
查看>>
POJ3304:Segments——题解
查看>>
48.EXt.Data.JsonReader()
查看>>
UML关系图
查看>>
一个action读取另一个action里的session
查看>>
leetcode 175. Combine Two Tables
查看>>
如何给一个数组对象去重
查看>>
Guava包学习-Cache
查看>>
2019-06-12 Java学习日记之JDBC
查看>>
灯箱效果(点击小图 弹出大图集 然后轮播)
查看>>
linux c 笔记 线程控制(二)
查看>>
samba服务器配置
查看>>
vue.js笔记
查看>>
【Unity3D入门教程】Unity3D之GUI浅析
查看>>
Hive 简单操作
查看>>
湘潭1247 Pair-Pair(树状数组)
查看>>
idea 不能粘贴复制问题
查看>>
IEnumerable<T>
查看>>