EntityFramework Code-First 簡易教程(七)-------領域類配置之Fluent API

Fluent API配置:

前面咱們已經瞭解到使用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

 

1、EntityTypeConfiguration類:

EntityTypeConfiguration類在Fluent API中有着重要的做用,它提供了一系列重要的方法和屬性來覆寫默認約定。學習

EntityTypeConfiguration類能夠運行DbModelBuilder類的Entity<TEntity>()方法得到,以下所示:ui

EntityTypeConfiguration

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配置實體類

2、Entity Mappings:

咱們繼續使用在學校應用裏面的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架構,因此生成的數據庫以下所示:

table mapping with fluent api Entity Framework code-first

映射實體到多張表:

下面的代碼演示了怎樣把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實體分裂成了兩張表,生成的數據庫以下所示:

multiple table mapping with fluent api Entity Framework code-first

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");

        }
    }
}
    
View Code

 

 

3、Property Mappings:

下面咱們來介紹怎樣用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) 可使用不一樣的方法來配置特別的屬性,以下圖所示:

configure property with fluent api Entity Framework code-first

爲屬性配置可空或不爲空的列:

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的主要內容就講的差很少了,下面開始會講一對一,一對多,多對多的領域類配置,和數據庫遷移等。

相關文章
相關標籤/搜索