EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射
I.EF里的默认映射
上篇文章演示的通过定义实体类就可以自动生成数据库,并且EF自动设置了数据库的主键、外键以及表名和字段的类型等,这就是EF里的默认映射。具体分为:
- 数据库映射:Code First 默认会在本地的SQL Expression数据库中建立一个和DbContext的子类的全名相同的数据库,全名指的是命名空间加上类名;
- 表映射:Code First 默认会按照类型名复数建立数据表,比如说Destination类对应的表名就叫Destinations;
- 列映射:Code First 默认会按照类中的属性名建立column,它还有默认的数据类型映射习惯,int会映射为interger,string会映射为 nvarchar(max),decimal会映射为decimal(18,2);
- 主键映射:Code First 默认会在类的属性中需找名字为Id或类型名称+Id的int类型的属性作为主键,并且是自增字段。
摘自这里
默认的映射一般只是简单映射,方便使用罢了。当然这些都是可以进行修改的,请往下看。
II.使用Data Annotations和Fluent API配置数据库的映射
Data Annotations翻译过来就是数据注解,是通过直接在实体类的属性上加注类似标签的东西达到对数据库的映射;
Fluent API翻译过来就是流利的API,Fluent API是在DbContext中定义数据库配置的一种方式。要使用Fluent API 就必须在你自定义的继承自DbContext的类中重载OnModelCreating这个方法。注意:
- Data Annotations和Fluent API任选其一就可以了,不需要同时配置;
- 使用Data Annotations需添加引用:using System.ComponentModel.DataAnnotations; 更具体的引用请参考本章结尾提供的源码。
实战:
1.Data Annotations:
设置Destination表的Name不为null:
[Required]
public string Name { get; set; }
很简单,直接在属性上加[Required]标注即可;
2.Fluent API:
用Fluent api必须重写OnModelCreating方法,我们在上下文类里重写下OnModelCreating方法并添加不为空的配置:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CodeFirst.Model.Destination>().Property(d => d.Name).IsRequired();
}
方法分析:先找到需要配置的实体类,然后点Property就是点出属性,=>是lambda表达式的写法,找到Name属性,然后调用IsRequired方法设置不为null。初见这东西肯定不好理解,多写写就熟悉了。
两种配置方式个人更喜欢Fluent API的方式,故放出的demo中Data Annotations也是有的,不过都被注释了。随着开发的深入,有些东西还是必须用Fluent API的方式才能配置出来的。
注意:我的类库都修改了默认命名空间(右键类库 - 属性),都加了个CodeFirst. 方便区分,其他类库中调用,我也是习惯用完整的命名空间.类库再.实体类来调用。
思考:使用Fluent API方式配置,每次都需要在OnModelCreating方法里写上一行配置,这样一个实体类如果有3个属性需要配置,10个实体类就需要配置30个,那么就得在OnModelCreating方法里写30行,很麻烦且不易维护。
解决办法:注意返回值可以看出modelBuilder的Entity<>泛型方法的返回值是EntityTypeConfiguration<>泛型类。我们可以定义一个继承自EntityTypeConfiguration<>泛型类的类来定义domain中每个类的数据库配置。
ok,我们在DataAccess类库下新建一个继承自EntityTypeConfiguration<>泛型类的DestinationMap类,在构造函数里写上配置:
public class DestinationMap : EntityTypeConfiguration<CodeFirst.Model.Destination>
{
public DestinationMap()
{
Property(d => d.Name).IsRequired();
}
}
需添加引用:using System.Data.Entity.ModelConfiguration;
这样,以后Destination需要配置的都来这里配置,然后添加到上下文中的OnModelCreating方法即可:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DestinationMap());
}
其他类的配置也是这样,先添加一个类名+Map的方法(命名随意),然后添加到OnModelCreating方法。
随意配置一些属性,然后跑下程序看生成的数据库:
可见:Name列已经不可空,Description也设置了长度不超过500等等。
注意:如果重新跑程序生成数据库报这个错:
The model backing the 'BreakAwayContext' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the DropCreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data.
意思就是,当前数据库正在使用中,无法删除再重新生成。这个时候断开数据库连接,再跑下程序就可以了。以后每次配置实体类再重新跑程序都需要断开数据库连接。
不知道大家还记不记得第一篇文章讲的EdmMetadata这个表,EF会自动生成这个表。这个表是监控实体类变化的,其实是个诟病,每次操作数据库都会发访问这张表的sql到数据库,用sql Profiler跟踪下即可发现如下sql:
2013.08.07补充:感谢园长dudu在回复中(4楼)的纠正,不是每次操作数据库都会发送sql到数据库,只是在EF初始化的时候会发送访问EdmMetadata表的sql到数据库。另推荐dudu的两篇文章供大家阅读:让Entity Framework不再私闯sys.databases 揭开Entity Framework LINQ查询的一点面纱
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[ModelHash] AS [ModelHash]
FROM [dbo].[EdmMetadata] AS [Extent1]
ORDER BY [Extent1].[Id] DESC
注:sql Profiler是一个监控发送到数据库sql语句的工具。sql server 2008自带,操作简单,如果不会请自行搜索相关资料。如果没有这个工具,那么是数据库版本不对或者数据库装出了问题。sql Profiler这工具后面讲一对一、一对多、多对多的各种操作时会经常使用到,也是我们调试EF语句性能的好帮手:
扩展:可以在OnModelCreating方法里添加:
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();//移除复数表名的契约
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();//防止黑幕交易 要不然每次都要访问
需引入命名空间:
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Data.Entity.Infrastructure;
第一句是移除复数表名的契约,就是EF默认生成的表名都是实体类的复数形式,有这句,表名就是实体类的名字了,不会再加个s了,当然你也可以通过强大的Fluent API配置这个。
第二句就是移除对EdmMetadata表的访问的,下次再操作数据库,就不会有访问EdmMetadata表的sql了。
注意:如果之前已经有数据库了,那么再加上移除对EdmMetadata表访问的配置再跑程序会报一个NotSupportedException的错:
Model compatibility cannot be checked because the EdmMetadata type was not included in the model. Ensure that IncludeMetadataConvention has been added to the DbModelBuilder conventions.
这个时候,先把BreakAwayConfigFile数据库分离,然后在本地删除掉数据库文件,再跑程序就可以了。如果不知道sql server的mdf和ldf文件的默认路径,请自行搜索。
结果跑了下程序,的确没有产生对EdmMetadata表访问的sql了,但是sql Profiler监控到了更多的sql语句被发送到了数据库。当初为了少一个访问EdmMetadata表的sql,结果多了更多的sql,并且如果再次生成数据库还必须要手动分离现有数据库并删除硬盘上的数据库文件。这里使不使用还是有待商量的,暂时注释掉:
//modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
思考:EF小组设计EdmMetadata表肯定有它的道理,移除对其的访问也不一定是科学的。这里仅做学习演示,真正的项目中可能也很少用Code First的方式生成数据库的。至少4.1版本下的EF使用Code First是有缺陷的,每次都要干掉所有数据,这个肯定不合适。后续版本的EF有数据迁徙的功能能解决这个,目前只是学习4.1这个经典的EF版本。
补充:实际开发中还是database First的方式比较主流。
上面只演示了简单的Data Annotations和Fluent API的一些简单配置,其实还有很多,下面列出部分常用,初学者应该都试着敲敲,然后对照着生成的数据库好好学习下如何配置:
//【主键】
//Data Annotations:
[Key]
public int DestinationId { get; set; } //Fluent API:
public class BreakAwayContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Destination>().HasKey(d => d.DestinationId);
}
} //【外键】
//Data Annotations:
public int DestinationId { get; set; }
[ForeignKey("DestinationId")]
public Destination Destination { get; set; } //Fluent API:
modelBuilder.Entity<Lodging>().HasRequired(p => p.Destination).WithMany(p=>p.Lodgings).HasForeignKey(p => p.DestinationId); //【长度】
//Data Annotations:通过StringLength(长度),MinLength(最小长度),MaxLength(最大长度)来设置数据库中字段的长度
[MinLength(),MaxLength()]
public string Name { get; set; }
[StringLength()]
public string Country { get; set; } //Fluent API:没有设置最小长度这个方法
modelBuilder.Entity<Destination>().Property(p => p.Name).HasMaxLength();
modelBuilder.Entity<Destination>().Property(p => p.Country).HasMaxLength(); //【非空】
//Data Annotations:
[Required(ErrorMessage="请输入描述")]
public string Description { get; set; } //Fluent API:
modelBuilder.Entity<Destination>().Property(p => p.Country).IsRequired(); //【数据类型】
Data Annotations:
将string映射成ntext,默认为nvarchar(max)
[Column(TypeName = "ntext")]
public string Owner { get; set; } //Fluent API:
modelBuilder.Entity<Lodging>().Property(p => p.Owner).HasColumnType("ntext"); //【表名】
//Data Annotations:
[Table("MyLodging")]
public class Lodging
{
} //Fluent API
modelBuilder.Entity<Lodging>().ToTable("MyLodging"); //【列名】
//Data Annotations:
[Column("MyName")]
public string Name { get; set; } //Fluent API:
modelBuilder.Entity<Lodging>().Property(p => p.Name).HasColumnName("MyName"); //【自增长】
//Data Annotations
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] Guid类型的主键、自增长
public Guid SocialId { get; set; } //Fluent API:
modelBuilder.Entity<Person>().Property(p => p.SocialId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); //【忽略列映射】
//Data Annotations:
[NotMapped]
public string Name
{
get
{
return FirstName + " " + LastName;
}
} //Fluent API:
modelBuilder.Entity<Person>().Ignore(p => p.Name); //【忽略表映射】
//Data Annotations:
[NotMapped]
public class Person
{ } //Fluent API:
modelBuilder.Ignore<Person>(); //【时间戳】
//Data Annotations:Timestamp
[Timestamp]
public Byte[] TimeStamp { get; set; } 只能是byte类型 //Fluent API:
modelBuilder.Entity<Lodging>().Property(p => p.TimeStamp).IsRowVersion(); //【复杂类型】
//Data Annotations:
[ComplexType]
public class Address
{
public string Country { get; set; }
public string City { get; set; }
} //Fluent API:
modelBuilder.ComplexType<Address>();
部分摘自这里
目前阶段看这个的确稍显复杂,仅做了解和方便后期查阅。后续讲一对一、一对多和多对多的关系时反复的手写Fluent API的时候就会很好理解了。
源码:点击下载
EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射的更多相关文章
- EF——默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射 02 (转)
EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射 I.EF里的默认映射 上篇文章演示的通过定义实体类就可以自动生成数据库,并且EF自动设置了数据库 ...
- EF的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射
I.EF的默认映射 上节我们创建项目,通过定义实体类就可以自动生成数据库,并且EF帮我们自动设置了数据库的主键.外键以及表名和字段的类型等,这就是EF的默认映射.具体分为: 数据库映射:Code Fi ...
- EF——使用Data Annotations和Fluent API配置数据库的映射配置 02.01(转)
要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...
- EF使用Fluent API配置映射关系
定义一个继承自EntityTypeConfiguration<>泛型类的类来定义domain中每个类的数据库配置,在这个自定义类的构造函数中使用我们上次提到的那些方法配置数据库的映射. 映 ...
- 使用 Fluent API 配置/映射属性和类型(摘自微软Data Access and Storage)
使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...
- 使用Fluent API 配置/映射属性和类型
Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code F ...
- 使用 Fluent API 配置/映射属性和类型
使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...
- 10.翻译系列:EF 6中的Fluent API配置【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/fluent-api-in-code-first.aspx EF 6 Code-Firs ...
- EF CodeFirst方式 Fluent Api配置
一.One-to-One Relationship[一对一关系] 两个表之间,只能由一个记录在另外一个表中.每一个主键的值,只能关联到另外一张表的一条或者零条记录.请记住,这个一对一的关系不是非常的普 ...
随机推荐
- mongodb安装&简单使用
转自Mac下使用brew安装mongodb,按着步骤已成功安装. brew常用命令 1.更新brew本身 brew update 2.使用brew安装软件 1 brew install soft_na ...
- yii2获取登录前的页面url地址--电脑和微信浏览器上的实现以及yii2相关源码的学习
对于一个有登录限制(权限限制)的网站,用户输入身份验证信息以后,验证成功后跳转到登录前的页面是一项很人性化的功能.那么获取登录前的页面地址就很关键,今天在做一个yii2项目的登录调试时发现了一些很有意 ...
- docker对数据卷进行还原操作
转载请注明出处 数据卷容器备份数据后,备份数据查看 http://www.cnblogs.com/zhuxiaojie/p/5947138.html 我们可能要把这个备份的数据,还原到另一台的do ...
- ASP.NET Core 中文文档 第三章 原理(12)托管
原文:Hosting 作者:Steve Smith 翻译:娄宇(Lyrics) 校对:何镇汐.许登洋(Seay) 为了运行 ASP.NET Core 应用程序,你需要使用 WebHostBuilder ...
- ASP.NET MVC Controller的激活
最近抽空看了一下ASP.NET MVC的部分源码,顺带写篇文章做个笔记以便日后查看. 在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,因此,说起Controll ...
- 判断IEnumerable<T>集合中是否包含有T对象
比如,有角色集合中,只有用户创建有角色,才出现“分配”铵钮.反之,隐藏. IEnumerable有一个方法,叫Any:
- spring笔记6 spring IOC的中级知识
1,spring ioc的整体流程,xml配置 spring ioc初始化的流程结合上图 步骤编号 完成的工作 1 spring容器读取配置文件,解析称注册表 2 根据注册表,找到相应的bean实现类 ...
- tornado+sqlalchemy+celery,数据库连接消耗在哪里
随着公司业务的发展,网站的日活数也逐渐增多,以前只需要考虑将所需要的功能实现就行了,当日活越来越大的时候,就需要考虑对服务器的资源使用消耗情况有一个清楚的认知. 最近老是发现数据库的连接数如果 ...
- 2016 ICPC青岛站---k题 Finding Hotels(K-D树)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5992 Problem Description There are N hotels all over ...
- java的原子性操作有哪些
Java中的原子操作包括:1)除long和double之外的基本类型的赋值操作2)所有引用reference的赋值操作3)java.concurrent.Atomic.* 包中所有类的一切操作coun ...