前面咱们已经了解到使用DataAnotations特性来覆写Code-First默认约定,如今咱们来学习Fluent API。数据库
Fluent API是另外一种配置领域类的方法,它比DataAnnotations特性提供更多的配置方法,下表是Fluent API支持的类型映射。express
映射种类 | 配置数据库 |
---|---|
模型(Model-wide)映射 |
|
实体(Entity)映射 |
|
属性(Property)映射 |
|
下面,咱们开始使用Fluent API来配置领域类。api
咱们首先建立Student和Standard两个领域类,一样也建立出DbContext类,DbContext类中有个OnModelCreating方法,这里咱们在它的继承类中把它覆写出来。架构
代码以下:并发
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure domain classes using modelBuilder here base.OnModelCreating(modelBuilder); } }
使用Fluent API配置领域类时,全部的配置代码都要写在OnModelCreating方法里面,全部的领域类均可以在这个方法里面写上他们的初始化代码。程序初始化的时候,DataAnnotation和Fluent API的优先级是:Fluent API > DataAnnotations > 默认约定。app
DbModelBuilder类包含了重要的用于配置的属性和方法,更多详情请翻阅MSDN文档。dom
接下来咱们详细讲一些经常使用的Fluent API配置方法。ide
EntityTypeConfiguration类在Fluent API中有着重要的做用,它提供了一系列重要的方法和属性来覆写默认约定。学习
EntityTypeConfiguration类能够运行DbModelBuilder类的Entity<TEntity>()方法得到,以下所示:ui
EntityTypeConfiguration有下面这些重要的方法:
方法名 | 返回类型 | 描述 |
---|---|---|
HasKey<TKey> | EntityTypeConfiguration | 为这个实体类型配置主键 |
HasMany<TTargetEntity> | ManyNavigationPropertyConfiguration | 为实体类型配置多对多关系 |
HasOptional<TTargetEntity> | OptionalNavigationPropertyConfiguration | 为实体类配置可选关系。没有指定关系的实体类型的实例会被存入数据库。数据库里外键可为空(nullable)。 |
HasRequired<TTargetEntity> | RequiredNavigationPropertyConfiguration | 为实体类型配置必须关系。除非关系肯定,不然实体类型的实例不能存入数据库。数据库中的外键将不能为空(non-nullable)。 |
Ignore<TProperty> | Void | 从模型中排除一个属性,这个属性将不会映射到数据库。 |
Map | EntityTypeConfiguration | 容许高级配置有关该实体类型映射到数据库模式。 |
Property<T> | StructuralTypeConfiguration | 配置一个定义了这种类型的结构属性 |
ToTable | Void | 配置实体类型映射的表名 |
能够访问MSDN查询更多关于 EntityTypeConfiguration 类的信息。
下面介绍怎么用Fluent API配置实体类
咱们继续使用在学校应用里面的Student和Standard两个领域类
代码以下:
public class Student { public Student() { } public int StudentID { get; set; } public string StudentName { get; set; } public DateTime? DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } public Standard Standard { get; set; } } public class Standard { public Standard() { } public int StandardId { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } } }
当咱们想为一组特殊的表设置一个不一样的构架时,可使用HasDefaultSchema方法:
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure default schema modelBuilder.HasDefaultSchema("Admin"); } }
Code-First将会为context类中的全部DbSet属性建立数据库表,表名就是属性名,好比上面的Students和Standards。咱们也能够给表配置一个不一样于DbSet属性名的表名,以下代码所示:
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure default schema modelBuilder.HasDefaultSchema("Admin"); //Map entity to table modelBuilder.Entity<Student>().ToTable("StudentInfo"); modelBuilder.Entity<Standard>().ToTable("StandardInfo","dbo"); } } }
如上代码,咱们配置表名的ToTable方法是接在Entity<TEntity>()方法后面,上面代码已经把映射Student实体的表名改为StudentInfo,把映射Standard实体的表名改为了StandardInfo,特别留意的是,虽然咱们把默认的架构名改为了Admin,可是Standard实体又特别指定了dbo架构,因此生成的数据库以下所示:
下面的代码演示了怎样把Student实体映射到数据库的多张表:
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Map(m => { m.Properties(p => new { p.StudentId, p.StudentName}); m.ToTable("StudentInfo"); }).Map(m => { m.Properties(p => new { p.StudentId, p.Height, p.Weight, p.Photo, p.DateOfBirth}); m.ToTable("StudentInfoDetail"); }); modelBuilder.Entity<Standard>().ToTable("StandardInfo"); } } }
如上所示,使用Map()方法能够映射Student实体的一些属性到StudentInfo表中,另外一些属性到StudentInfoDetail表中,咱们把Student实体分裂成了两张表,生成的数据库以下所示:
Map method need the delegate method as a parameter. You can pass or in Map method, as shown below.
Map方法的传入参数是一个委托,具体能够参考 Action delegate 和 lambda expression。
完成代码以下:
using System.Data.Entity.ModelConfiguration.Configuration; namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Map(delegate(EntityMappingConfiguration<Student> studentConfig) { studentConfig.Properties(p => new { p.StudentId, p.StudentName }); studentConfig.ToTable("StudentInfo"); }); Action<EntityMappingConfiguration<Student>> studentMapping = m => { m.Properties(p => new { p.StudentId, p.Height, p.Weight, p.Photo, p.DateOfBirth }); m.ToTable("StudentInfoDetail"); }; modelBuilder.Entity<Student>().Map(studentMapping); modelBuilder.Entity<Standard>().ToTable("StandardInfo"); } } }
下面咱们来介绍怎样用Fluent API配置实体类的属性
咱们仍然使用学校的例子,以下两个Student和Standard领域类:
public class Student { public Student() { } public int StudentKey { get; set; } public string StudentName { get; set; } public DateTime DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } public Standard Standard { get; set; } } public class Standard { public Standard() { } public int StandardKey { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } } }
上面的两个领域类,不能依据Code-First默认约定生成主键,由于它们没有Id或者{类名}+Id的属性,因此这里使用EntityTypeConfiguration类里的HasHey()方法来建立主键。
注意,modelBuilder.Entity<TEntity>()返回的是EntityTypeConfiguration对象。
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure primary key modelBuilder.Entity<Student>().HasKey<int>(s => s.StudentKey); modelBuilder.Entity<Standard>().HasKey<int>(s => s.StandardKey); //Configure composite primary key modelBuilder.Entity<Student>().HasKey<int>(s => new { s.StudentKey, s.StudentName }); } }
Code-First默认约定以属性名为列名,下面的代码覆写了这一约定:
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure Column modelBuilder.Entity<Student>() .Property(p => p.DateOfBirth) .HasColumnName("DoB") .HasColumnOrder(3) .HasColumnType("datetime2"); } }
如上所示,咱们使用一个实体的Property()方法来配置列名、排序和数据类型
modelBuilder.Entity<TEntity>().Property(expression)
可使用不一样的方法来配置特别的属性,以下图所示:
Code-First将为主键的数据类型建立一个不为空的值,由于主键自己就不能为空,除非它的属性上使用了?号或者标记了Nullable<T>。
使用IsOptional方法能够建立一个可为空的列,一样,使用IsRequired也能够建立一个不为空的列。
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure Null Column modelBuilder.Entity<Student>() .Property(p => p.Heigth) .IsOptional(); //Configure NotNull Column modelBuilder.Entity<Student>() .Property(p => p.Weight) .IsRequired(); } } }
Code-First默认约定是给列建立最大的数据类型大小,用HasMaxLength()方法覆写之。
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Set StudentName column size to 50 modelBuilder.Entity<Student>() .Property(p => p.StudentName) .HasMaxLength(50); //Set StudentName column size to 50 and change datatype to nchar //IsFixedLength() change datatype from nvarchar to nchar modelBuilder.Entity<Student>() .Property(p => p.StudentName) .HasMaxLength(50).IsFixedLength(); //Set size decimal(2,2) modelBuilder.Entity<Student>() .Property(p => p.Height) .HasPrecision(2, 2); } } }
IsFixedLength方法把列的类型从nvarchar转变为nchar,HasPrecision方法改变了数据类型为decimal列的精度。
使用ConcurrencyToken方法把一个属性设置为并发列,代码以下:
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Set StudentName as concurrency column modelBuilder.Entity<Student>() .Property(p => p.StudentName) .IsConcurrencyToken(); } } }
如上代码所示,StudentName被设置成为了并发列,每当update和delete操做的时候都会把StudentName中的值加入到SQL语句中的"where"子句中。
对于byte[]类型的属性,咱们也能够用IsRowVersion()方法来将其配置成并发列。
到此,Fluent API的主要内容就讲的差很少了,下面开始会讲一对一,一对多,多对多的领域类配置,和数据库迁移等。