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

一、表名和列名

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. rest-assured : Restful API 测试利器 - 真正的黑盒单元测试(跟Spring-Boot更配哦,更新至spring-boot1.4.1)

    { "Author":"tomcat and jerry", "URL" :"http://www.cnblogs.com/tom ...

  2. vps云服务器建站后绑定域名的方法?

    有很多的新手站长们,都不知道vps建站后该如何绑定自己的域名,这里就Windows系统的VPS主机利用iis绑定网站域名的方法,简要介绍一下. 通常情况下,我们在使用IIS建站的时候,都会有一步提示, ...

  3. [转]阿里云CentOS配置全过程

     

  4. Nodejs 高并发长链接TCP链接的服务器设计问题

    最近有个项目比较棘手,nodejs的tcp服务,目前的服务器支持3W左右的客户端连接,但是客户希望能够支持30W左右,原先的模型是让客户端请求一个地址分发服务器,然后再tcp链接到不同的地址上实现高并 ...

  5. Backbone.js

    Backbone.js是一套JavaScript框架與RESTful JSON的應用程式介面.也是一套大致上符合MVC架構的編程範型.Backbone.js以輕量為特色,只需依賴一套Javascrip ...

  6. 大前端学习笔记整理【六】this关键字详解

    前言 在上一篇博客里我总结了下辨认this指向的四种方式,但是有师兄抛出一个问题来,我发现那些this的指向并不能说明更复杂的情况,先看下这段代码 var a = { name: 'a', getNa ...

  7. Java 获取 Unix时间戳

    unix时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒. 在大多数的UNIX系统中UNIX时间戳存储为32位,这样会引发2038年问题. 但是,因为需求是需要int类 ...

  8. 利用onNewIntent避免Activity之间的跳转传值多次实体化

    onNewIntent 需要注意的是: 1.通过Intent启到一个Activity的时候,就算已经存在一个相同的正在运行的Activity,系统也会创建一个新的Activity实例.为了不让Acti ...

  9. windows环境下修改Mysql的root密码

    最近一直没用Mysql,root密码给忘了,然后就自己修改了一下,现在整理一下步骤.(我的版本是5.1) 启动MySQL服务:>>net start mysql 关闭MySQL服务:> ...

  10. Web大文件下载控件(down2)-示例更新-Xproer.HttpDownloader

    版权所有 2009-2016 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/down2/i ...