I.EF里的默认映射

上篇文章演示的通过定义实体类就可以自动生成数据库,并且EF自动设置了数据库的主键、外键以及表名和字段的类型等,这就是EF里的默认映射。具体分为:

  1. 数据库映射:Code First 默认会在本地的SQL Expression数据库中建立一个和DbContext的子类的全名相同的数据库,全名指的是命名空间加上类名;
  2. 表映射:Code First 默认会按照类型名复数建立数据表,比如说Destination类对应的表名就叫Destinations;
  3. 列映射:Code First 默认会按照类中的属性名建立column,它还有默认的数据类型映射习惯,int会映射为interger,string会映射为 nvarchar(max),decimal会映射为decimal(18,2);
  4. 主键映射:Code First 默认会在类的属性中需找名字为Id或类型名称+Id的int类型的属性作为主键,并且是自增字段。

摘自这里
默认的映射一般只是简单映射,方便使用罢了。当然这些都是可以进行修改的,请往下看。

II.使用Data Annotations和Fluent API配置数据库的映射

Data Annotations翻译过来就是数据注解,是通过直接在实体类的属性上加注类似标签的东西达到对数据库的映射;
Fluent API翻译过来就是流利的API,Fluent API是在DbContext中定义数据库配置的一种方式。要使用Fluent API 就必须在你自定义的继承自DbContext的类中重载OnModelCreating这个方法。注意:

  1. Data Annotations和Fluent API任选其一就可以了,不需要同时配置;
  2. 使用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 Code First 系列文章导航

  1. EF Code First 初体验
  2. EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射  本节源码
  3. EF里Guid类型数据的自增长、时间戳和复杂类型的用法  本节源码
  4. EF里一对一、一对多、多对多关系的配置和级联删除  本节源码
  5. EF里的继承映射关系TPH、TPT和TPC的讲解以及一些具体的例子  本节源码

EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射的更多相关文章

  1. EF——默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射 02 (转)

    EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射   I.EF里的默认映射 上篇文章演示的通过定义实体类就可以自动生成数据库,并且EF自动设置了数据库 ...

  2. EF的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射

    I.EF的默认映射 上节我们创建项目,通过定义实体类就可以自动生成数据库,并且EF帮我们自动设置了数据库的主键.外键以及表名和字段的类型等,这就是EF的默认映射.具体分为: 数据库映射:Code Fi ...

  3. EF——使用Data Annotations和Fluent API配置数据库的映射配置 02.01(转)

    要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...

  4. EF使用Fluent API配置映射关系

    定义一个继承自EntityTypeConfiguration<>泛型类的类来定义domain中每个类的数据库配置,在这个自定义类的构造函数中使用我们上次提到的那些方法配置数据库的映射. 映 ...

  5. 使用 Fluent API 配置/映射属性和类型(摘自微软Data Access and Storage)

    使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...

  6. 使用Fluent API 配置/映射属性和类型

    Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code F ...

  7. 使用 Fluent API 配置/映射属性和类型

    使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...

  8. 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 ...

  9. EF CodeFirst方式 Fluent Api配置

    一.One-to-One Relationship[一对一关系] 两个表之间,只能由一个记录在另外一个表中.每一个主键的值,只能关联到另外一张表的一条或者零条记录.请记住,这个一对一的关系不是非常的普 ...

随机推荐

  1. 网站文件系统发展&&分布式文件系统fastDFS

    网站文件系统发展 1.单机时代的图片服务器架构 初创时期由于时间紧迫,开发人员水平也很有限等原因.所以通常就直接在website文件所在的目录下,建立1个upload子目录,用于保存用户上传的图片文件 ...

  2. 【NLP】揭秘马尔可夫模型神秘面纱系列文章(二)

    马尔可夫模型与隐马尔可夫模型 作者:白宁超 2016年7月11日15:31:11 摘要:最早接触马尔可夫模型的定义源于吴军先生<数学之美>一书,起初觉得深奥难懂且无什么用场.直到学习自然语 ...

  3. 你真的会玩SQL吗?你所不知道的 数据聚合

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  4. golang枚举类型 - iota用法拾遗

    在c#.java等高级语言中,经常会用到枚举类型来表示状态等.在golang中并没有枚举类型,如何实现枚举呢?首先从枚举的概念入手. 1.枚举类型定义 从百度百科查询解释如下:http://baike ...

  5. 使用VS Code从零开始开发调试.NET Core 1.0

    使用VS Code 从零开始开发调试.NET Core 1.0. .NET Core 是一个开源的.跨平台的 .NET 实现. VS Code 全称是 Visual Studio Code,Visua ...

  6. enote笔记法使用范例(1)——自己总结的一些编写代码的常识 (a)

    章节. 编程习惯     why 函数(<<为了>>便于提升软件开发效率和维护效率) 开发角度: 1)隐藏实现细节,这也是API质量最重要的品质2)复用:通过使用函数来代码复用 ...

  7. 数据结构:堆排序 (python版) 小顶堆实现从大到小排序 | 大顶堆实现从小到大排序

    #!/usr/bin/env python # -*- coding:utf-8 -*- ''' Author: Minion-Xu 小堆序实现从大到小排序,大堆序实现从小到大排序 重点的地方:小堆序 ...

  8. php设计模式总结-工厂模式

    使用工厂模式的目的或目标? 工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象.减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码. 通俗的说,以前创建 ...

  9. JavaScript的“原型甘露”

    今天跟朋友讨论JS的面向对象编程问题,想起了原来曾经看过一篇文章,但是看过很久想不起来了,用了很多关键词,终于用“悟透JavaScript  面向对象”这两个关键词找到了“原文”,原文地址:http: ...

  10. Web Worker javascript多线程编程(一)

    什么是Web Worker? web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验. 一般来说Javascript ...