Programming Entity Framework CodeFirst--数据库约定和配置
这一章主要主要讲的是我们的模型如何映射到数据库,而不影响模型,以及不同的映射场景。
一、表名和列名
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.模型要映射到数据库中有三种方式。
前面两种我们前面都已经尝试过,对于第三种,不使用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)可获取的属性
如果想配置私有属性,这样就需要将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"));

也可以指定为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。


Resorts:

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--数据库约定和配置的更多相关文章
- 【读书笔记】Programming Entity Framework CodeFirst -- 初步认识
以下是书<Programming Entity Framework Code First>的学习整理,主要是一个整体梳理. 一.模型属性映射约定 1.通过 System.Component ...
- Programming Entity Framework CodeFirst -- 约定和属性配置
以下是EF中Data Annotation和 Fluenlt API的不同属性约定的对照. Length Data Annotation MinLength(nn) MaxLength(nn) ...
- Programming Entity Framework CodeFirst--表关系约定
表之间的关系分为一对多,多对多,一对一三种,实质就是对外键进行配置. 一.一对多 1. Required Destination包含Lodging>的集合. public class Desti ...
- entity framework codefirst 用户代码未处理DataException,InnerException基础提供程序在open上失败,数据库生成失败
警告:这是一个入门级日志,如果你很了解CodeFirst,那请绕道 背景:这篇日志记录我使用Entity FrameWork CodeFirst时出现的错误和解决问题的过程,虽然有点曲折……勿喷 备注 ...
- Entity Framework Codefirst的配置步骤
Entity Framework Codefirst的配置步骤: (1) 安装命令: install-package entityframework (2) 创建实体类,注意virtual关键字在导航 ...
- 第三篇:Entity Framework CodeFirst & Model 映射 续篇 EntityFramework Power Tools 工具使用
上一篇 第二篇:Entity Framework CodeFirst & Model 映射 主要介绍以Fluent API来实作EntityFramework CodeFirst,得到了大家一 ...
- 第二篇:Entity Framework CodeFirst & Model 映射
前一篇 第一篇:Entity Framework 简介 我有讲到,ORM 最关键的 Mapping,也提到了最早实现Mapping的技术,就是 特性 + 反射,那Entity Framework 实现 ...
- Entity Framework CodeFirst数据迁移
前言 紧接着前面一篇博文Entity Framework CodeFirst尝试. 我们知道无论是“Database First”还是“Model First”当模型发生改变了都可以通过Visual ...
- entity framework 删除数据库出现错误的解决方法--最土但是很有效的方法
无法删除数据库,因为该数据库当前正在使用. public ChinaerContext() : base("name=ContextConn") { // Database.Set ...
- Programming Entity Framework 翻译(2)-目录2-章节
How This Book Is Organized 本书组织结构 Programming Entity Framework, Second Edition, focuses on two ways ...
随机推荐
- sql报句柄无效。 (异常来自 HRESULT:0x80070006 (E_HANDLE))
是由于数据库连接资源被耗尽或者用完没被释放导致的. 我在字符串中加了启用连接池好了. 如果错误信息为:sql 无效操作.连接被关闭 也是这个问题导致的.
- visul studio 文件分包
1.搜索算法. 2.软件控制逻辑. 3.自定义控件. 4.GUI模块. 5.线程化操作
- sql server convert 日期
),) --2016/11 ),) --2016-11-03 17:46:47
- git使用--git命令项目提交问题总结
提交遇到Error "remote ref does not exist"解决办法:git fetch -p MY_REMOTE eg. git fetch -p o ...
- 框架介绍thinkphp
ThinkPHP是一个免费开源的,快速.简单的面向对象的 轻量级PHP开发框架 ,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的.ThinkPH ...
- C# 如何给sql数据库的日期字段插入空值
在C#中声明日期变量时用SqlDateTime类型,引用:using System.Data.SqlTypes; 例子:user.AbortDate = SqlDateTime.Null;
- Android Sqlite数据库相关——实现 Sqlite 数据库版本升级
继承SQLiteOpenHelper类后,实现其中的onUpgrade 方法 @Override public void onUpgrade(SQLiteDatabase db, int oldVer ...
- Sql Server插入数据并返回自增ID,@@IDENTITY,SCOPE_IDENTITY和IDENT_CURRENT的区别
预备知识:SQLServer的IDENTITY关键字IDENTITY关键字代表的是一个函数,而不是identity属性.在access里边没有这个函数,所以在access不能用这个语句.语法:iden ...
- Bootstrap 3 简介
Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first pr ...
- disconf安装部署
1.client pom文件引入 <dependency> <groupId>com.baidu.disconf</groupId> <artifactId& ...