这一章主要主要讲的是我们的模型如何映射到数据库,而不影响模型,以及不同的映射场景。

一、表名和列名

1.指定表名

[Table("PersonPhotos")]
public class PersonPhoto

[Table("Locations", Schema="baga")]
public class Destination

Schema修改数据库架构,默认是dbo。

API:

modelBuilder.Entity<Destination>().ToTable("Locations", "baga");

2.列名

[Column("LocationID")]
public int DestinationId { get; set; }
[Required, Column("LocationName")]
public string Name { get; set; }

API:

Property(d => d.Nam
.IsRequired().HasColumnName("LocationName");
Property(d => d.DestinationId).HasColumnName("LocationID");

二、表分割

将一个模型拆成两张表,比如Destination。

public class Destination
{
[Key]
public int DestinationId { get; set; }
[Required]
public string Name { get; set; }
public string Country { get; set; }
[MaxLength()]
public string Description { get; set; }
[Column(TypeName = "image")]
public byte[] Photo { get; set; }
public List<Lodging> Lodgings { get; set; }
}

API:(DataAnnotations不能处理子对象)

 modelBuilder.Entity<Destination>()
.Map(m =>
{
m.Properties(d => new {d.Name, d.Country, d.Description});
m.ToTable("Locations");
})
.Map(m =>
{
m.Properties(d => new {d.Photo});
m.ToTable("LocationPhotos");
});

运行后,Destination 拆分成了Locations和LocationPhotos

当Destination添加数据的时候,这个两个表的主键都会增加。

  

三、数据库映射控制

1.模型要映射到数据库中有三种方式。

   1).将对象加入到Dbset中。
   2).在别的类型中,引用当前类型。(Person包含PersonPhoto,PersonPhoto是不需要加人Dbset的。)
   3).通过API在DbModelBuilder方法中配置。

前面两种我们前面都已经尝试过,对于第三种,不使用Dbset 就需要使用EntityTypeConfiguration。 可以建立一个空的:

public class ReservationConfiguration :EntityTypeConfiguration<Reservation>
{}

再加入modelBuider

modelBuilder.Configurations.Add(new ReservationConfiguration());

2.忽略类型映射。

如果不想数据库映射某个类型,我们可以将其忽略掉。

[NotMapped]
public class MyInMemoryOnlyClass
//API:
modelBuilder.Ignore<MyInMemoryOnlyClass>();

3.属性映射类型

1)只能是EDM支持的类型。

Binary, Boolean, Byte, DateTime, DateTimeOffset, Decimal, Double, Guid, Int16, Int32, Int64, SByte, Single, String, Time

枚举类型现在已经支持了。MyType是个枚举类型,Flag是Uint型,不支持EF就自动忽略了。

2)可获取的属性

   .Public属性会自动映射。
   .Setter可以是限制访问,但Getter必须是Public。
   .如果想非Public的属性也映射,就需要通过API来配置。

如果想配置私有属性,这样就需要将Config类置于内部。如下,Name是private的,PersonConfig想要获取这个类型就需要置于Person内部了:

public class Person
{
public int PersonId { get; set; }
private string Name { get; set; }
public class PersonConfig : EntityTypeConfiguration<Person>
{
public PersonConfig()
{
Property(b => b.Name);
}
}
public string GetName()
{
return this.Name;
}
public static Person CreatePerson(string name)
{
return new Person { Name = name };
}
}

3)属性忽略

.没有setter

如果People包含FullName属性,EF是不会映射这个属性的。

public string FullName
{
get { return String.Format("{0} {1}", FirstName.Trim(), LastName); }
}

.直接忽略

 [NotMapped]
public string TodayForecast{get;set;}
//API:
Ignore(d => d.TodayForecast);

四、继承类型映射

1)默认继承 Table Per Hierarchy (TPH)  子类父类在一张表中。

public class Lodging
{
public int LodgingId { get; set; }
[Required]
[MaxLength()]
[MinLength()]
public string Name { get; set; }
[StringLength(, MinimumLength = )]
public string Owner { get; set; }
public bool IsResort { get; set; }
public Destination Destination { get; set; }
public int DestinationId { get; set; }
public List<InternetSpecial> InternetSpecials { get; set; }
[InverseProperty("PrimaryContactFor")]
public Person PrimaryContact { get; set; }
[InverseProperty("SecondaryContactFor")]
public Person SecondaryContact { get; set; } } public class Resort : Lodging
{
public string Entertainment { get; set; }
public string Activities { get; set; }
}

没有创建Resort表,而是Lodgings表中多了Restort的字段

而且还多了个Discriminator (辨别者)列,nvarchar(128) ,专门用来识别是哪个类型,插入2组数据。

  private static void InsertLodging()
{
var lodging = new Lodging
{
Name = "Rainy Day Motel",
Destination = new Destination
{
Name = "Seattle, Washington",
Country = "USA"
}
};
using (var context = new BreakAwayContext())
{
context.Lodgings.Add(lodging);
context.SaveChanges();
}
}
private static void InsertResort()
{
var resort = new Resort
{
Name = "Top Notch Resort and Spa",
Activities = "Spa, Hiking, Skiing, Ballooning",
Destination = new Destination
{
Name = "Stowe, Vermont",
Country = "USA"
}
};
using (var context = new BreakAwayContext())
{
context.Lodgings.Add(resort); //没有去添加Dbset<Resort>
context.SaveChanges();
}
}

同样我们可以定义discriminator的列名和类型的值。

 modelBuilder.Entity<Lodging>()
.Map(m =>
{
m.ToTable("Lodgings");
m.Requires("LodgingType").HasValue("Standard");
})
.Map<Resort>(m => m.Requires("LodgingType").HasValue("Resort"));
 这里Requires和Hasvalue都是来定义discriminator 列的。
 再次运行,列名和类型值也都已经改变。

也可以指定为bool类型。将这个任务交给IsResort

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

要注意的是Lodging模型中不能再包含IsResort属性,在模型验证的时候就出错,EF它分不清这个IsResort和识别类型IsResort是不是同一个。不然会引发异常。

2)Table Per Type (TPT) Hierarchy (分开存储,派生类只存储自己独有的属性)

给派生类加上表名就是TPT了。

[Table("Resorts")]
public class Resort : Lodging
{
public string Entertainment { get; set; }
public string Activities { get; set; }
}

这样EF会创建一个新表,并拥有Lodging的key。

是插入两条数据:
Lodgings

Resorts:

API:
modelBuilder.Entity<Lodging>()
.Map<Resort>(m =>
{
m.ToTable("Resorts");
}
);
可以写在一起。
modelBuilder.Entity<Lodging>().Map(m =>
{
m.ToTable("Lodgings");
}).Map<Resort>(m =>
{
m.ToTable("Resorts");
});

3)Table Per Concrete Type (TPC) Inheritance  父类和派生各自拥有全部属性

好比Resorts作为一个表拥有所有的属性。只能通过API的MapInheritedProperties来实现。且ToTable方法不能少。

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

这个时候运行会出错:

TPC要求使用TPC的类如果被引用必须有一个显示的外键属性。就像Lodging中的DestinationId对于Destination

public class Lodging
{
public int LodgingId { get; set; }
[Required]
[MaxLength()]
[MinLength()]
public string Name { get; set; }
[StringLength(, MinimumLength = )]
public string Owner { get; set; }
// public bool IsResort { get; set; }
public Destination Destination { get; set; }
public int DestinationId { get; set; }
public List<InternetSpecial> InternetSpecials { get; set; }
public Person PrimaryContact { get; set; }
public Person SecondaryContact { get; set; }
}

这里的PrimaryContact 和 SecondaryContact 没有指明外键。需要强制给它加上外键。这里会让有的人难受,因为EF的规则而去要改变自己的领域模型。

public class Lodging
{
//.....
public int? PrimaryContactId { get; set; }
public Person PrimaryContact { get; set; }
public int? SecondaryContactId { get; set; }
public Person SecondaryContact { get; set; }
}

这个时候还没完,EF并不清楚这些外键。需要再配置。

 modelBuilder.Entity<Lodging>()
.Map(m => m.ToTable("Lodgings"))
.Map<Resort>(m =>
{
m.ToTable("Resorts");
m.MapInheritedProperties();
});
modelBuilder.Entity<Lodging>().HasOptional(l => l.PrimaryContact)
.WithMany(p => p.PrimaryContactFor)
.HasForeignKey(p => p.PrimaryContactId);
modelBuilder.Entity<Lodging>().HasOptional(l => l.SecondaryContact)
.WithMany(p => p.SecondaryContactFor)
.HasForeignKey(p => p.SecondaryContactId);

生成的表如下。都带有三个外键。

对于基类是抽象类型,对EF来说没有多大的区别。至于这三种该怎么用。这里有博客:http://blogs.msdn.com/b/alexj/archive/2009/04/15/tip-12-choosing-an-inheritance-strategy.aspx

各种表现上面还是TPT最佳。

Programming Entity Framework CodeFirst--数据库约定和配置的更多相关文章

  1. 【读书笔记】Programming Entity Framework CodeFirst -- 初步认识

    以下是书<Programming Entity Framework Code First>的学习整理,主要是一个整体梳理. 一.模型属性映射约定 1.通过 System.Component ...

  2. Programming Entity Framework CodeFirst -- 约定和属性配置

     以下是EF中Data Annotation和 Fluenlt API的不同属性约定的对照.   Length Data Annotation MinLength(nn) MaxLength(nn) ...

  3. Programming Entity Framework CodeFirst--表关系约定

    表之间的关系分为一对多,多对多,一对一三种,实质就是对外键进行配置. 一.一对多 1. Required Destination包含Lodging>的集合. public class Desti ...

  4. entity framework codefirst 用户代码未处理DataException,InnerException基础提供程序在open上失败,数据库生成失败

    警告:这是一个入门级日志,如果你很了解CodeFirst,那请绕道 背景:这篇日志记录我使用Entity FrameWork CodeFirst时出现的错误和解决问题的过程,虽然有点曲折……勿喷 备注 ...

  5. Entity Framework Codefirst的配置步骤

    Entity Framework Codefirst的配置步骤: (1) 安装命令: install-package entityframework (2) 创建实体类,注意virtual关键字在导航 ...

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

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

  7. 第二篇:Entity Framework CodeFirst & Model 映射

    前一篇 第一篇:Entity Framework 简介 我有讲到,ORM 最关键的 Mapping,也提到了最早实现Mapping的技术,就是 特性 + 反射,那Entity Framework 实现 ...

  8. Entity Framework CodeFirst数据迁移

    前言 紧接着前面一篇博文Entity Framework CodeFirst尝试. 我们知道无论是“Database First”还是“Model First”当模型发生改变了都可以通过Visual ...

  9. entity framework 删除数据库出现错误的解决方法--最土但是很有效的方法

    无法删除数据库,因为该数据库当前正在使用. public ChinaerContext() : base("name=ContextConn") { // Database.Set ...

  10. Programming Entity Framework 翻译(2)-目录2-章节

    How This Book Is Organized 本书组织结构 Programming Entity Framework, Second Edition, focuses on two ways ...

随机推荐

  1. fool

    from PIL import Imageimg = Image.open("D:\\pic2\\CZA3302.png")(w,h) = img.sizeim=img.conve ...

  2. SGA(System Global Area)

    系统激活时在内存内规划的一个固定的区域,用于存储每位使用者所需存取的数据和必备的系统信息.这个区域成为系统全局区. 数据块缓存区:存放读取数据文件的数据块副本,或者曾经处理过的数据.有效减少读取数据时 ...

  3. jquery中是否加()的问题

    自己总结的,慢慢修改再: 1带上()代表立即执行 去掉()代表当有事件发生的时候,我再执行

  4. Win10专业版激活永久可查激活信息

    Win10专业版激活永久步骤 ------在安装Win10专业版后,使用激活工具将系统激活到180天 这里附带在下屡试不爽的激活工具--百度云盘-- 链接:http://pan.baidu.com/s ...

  5. SVN版本控制系统

    SVN 版本控制系统 1.SVN作用 防止代码丢失 : 因为没有哪个项目能够一次性开发完成 代码版本回退 : 你可以在开发过程中找到以前上传到服务器上面的所有版本 多人代码整合 : 公司中多个人开发同 ...

  6. oracle 小题

    create table student(sno varchar2(10) primary key,sname varchar2(20),sage number(2),ssex varchar2(5) ...

  7. React jQuery公用组件开发模式及实现

    目前较为流行的react确实有很多优点,例如虚拟dom,单向数据流状态机的思想.还有可复用组件化的思想等等.加上搭配jsx语法和es6,适应之后开发确实快捷很多,值得大家去一试.其实组件化的思想一直在 ...

  8. ELK 5.0 组件后台启动

    elasticsearch 后台启动,只需要 在bin目录下执行: ./elasticsearch -d 查看是否启动成功使用: ps aux|grep elasticsearch kibana 后台 ...

  9. 在64位windows下使用instsrv.exe和srvany.exe创建windows服务[转]

    本文转自:https://www.iflym.com/index.php/computer-use/201205020001.html 在32位的windows下,包括windows7,windows ...

  10. Framework manager编写SQL错误整理

    BMT-MD-0059 这个报错是由于导入了表全部的列,而之引用了部分列,所以未被引用的列将要被删除 XQE-PLN-0248 在模型中找不到“MONTHLY_FORECAST_FACT”的列“mon ...