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


Fluent API配置:

前面我們已經了解到使用DataAnotations特性來覆寫Code-First默認約定,現在我們來學習Fluent API。

Fluent API是另一種配置領域類的方法,它比DataAnnotations特性提供更多的配置方法,下表是Fluent API支持的類型映射。

映射種類 配置數據庫
模型(Model-wide)映射
  • 設置默認架構
  • 設置自定義約定
實體(Entity)映射
  • 設置單表或多表和設置架構
  • 設置復雜類型
  • 設置繼承層次結構
屬性(Property)映射
  • 設置列,列名、類型、是否可空、大小、排序
  • 設置並發列
  • 設置外鍵列
  • 配置關系

下面,我們開始使用Fluent 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 > 默認約定。

DbModelBuilder類包含了重要的用於配置的屬性和方法,更多詳情請翻閱MSDN文檔。

接下來我們詳細講一些常用的Fluent API配置方法。

 

一、EntityTypeConfiguration類:

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

EntityTypeConfiguration類可以運行DbModelBuilder類的Entity<TEntity>()方法獲得,如下所示:

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

二、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

 

 

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM