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

一、表名和列名

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. ListView13添加2

    Columns=//添加列总行的标题 GridLines=true //显示网格线 添加数据------------- listView1.Items.Add("123123123" ...

  2. 建站阿里云、amh主机面版

    阿里云 Nginx+tomcat7+Mencached负载均衡集群配置 http://blog.csdn.net/zht666/article/details/38515147 apache2.2.1 ...

  3. php-4种排序

    <?php$arr = array(1, 43, 54, 62, 21, 66, 32, 78, 36, 76, 39); //1. 冒泡排序 //在要排序的一组数中,对当前还未排好的序列,从前 ...

  4. cannot determine the location of the vs common tools folder

    问题:打开"VS2010开发人员命令提示后",上面提示"cannot determine the location of the vs common tools fold ...

  5. kali linux 、 windows、ubuntu三系统的引导问题

    '小飞机'是一个学生,所以接触的东西,虽广泛,但并不精通,在此利用随笔,记录自己的一些学习过程,以及自己的想法,既可以有时间自己复习,也可以顺便帮助别人. 近期由于同学的引诱以及男生天生对于破解的好奇 ...

  6. STM32 DAC的配置与使用

    本博文转自:http://blog.chinaunix.net/uid-24219701-id-4101802.html STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输 ...

  7. ExtJS入门实例

    一.去官网下载EXTJS包extjs5,这里采用的是5.0版本! 二.解压extjs包,找到 ext-all.js基础包(\ext-5.0.0\build): ext-all-debug.js基础包, ...

  8. FibonacciSequence

    import java.util.Scanner; public class Fibonacci { public static void main(String[] args) { int n; f ...

  9. VMware 12 的vmware tools安装和如何使用(系统是CENTOS6.5)

    1.用了一下虚拟机vmware12,但是总是没法使用它的vmware Tool ,网上一直说在哪个哪个文件夹,其实并没有 2.于是我用命令行找到了在系统中的VMware Tools 3.首先,保证li ...

  10. 无法打开注册表项 unknown 没有足够的权限访问

    secedit /configure /cfg %windir%\inf\defltbase.inf /db defltbase.sdb /verbose 执行完,重新安装即可.