1. 程式人生 > >Entity Framework 查漏補缺 (三)

Entity Framework 查漏補缺 (三)

文章 ignore tro assembly nat ron def first require

Code First的數據庫映射

有兩種方式來實現數據庫映射:

  • 數據屬性:Data Annotation
  • 映射配置: Fluent API

有繼承關系的實體如何映射?

  • Code First在生成數據庫表時,默認使用TPH方式

就是把父類和子類生成同一張表,額外增加了一列Discriminator字段,區分是父類或子類的數據類型

比如:

父類Book對象

public class Book
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int
BookID { get; set; } public string BookName { get; set; } public int Pages { get; set; } }

子類HistoryBooks對象,繼承Book

public class HistoryBooks:Book
{
    public int Chapter { get; set; }
}

數據庫生成一張表

技術分享圖片

  • 另一種方式(Data Annotation實現):TPT

不管父類子類,各自生成一張表,以及在子類中增加兩者聯系的外鍵;

定義TPT方式 父類和子類定義 [Table("XXX")]

如:

父類

[Table("Book")]
public class Book
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int BookID { get; set; }
    public string BookName { get; set; }
    public int Pages { get; set; }
}

子類

[Table("HistoryBooks")]
public class HistoryBooks:Book
{
public int Chapter { get; set; } }

映射數據生成的兩張表

Book:

技術分享圖片

HistoryBooks:

技術分享圖片

映射方式一:Data Annotation

給實體對象的屬性加上註解特性,實現與數據庫之間建立映射關系並進行控制

如:

技術分享圖片

Booid映射到表中字段為自增的主鍵

DataAnnotations 包含的常用特性:

KeyAttribute:對應數據庫中表的主鍵的設置

RequiredAttribute:對應數據庫中字段的數據不可null

MaxLengthAttribute:對應數據庫中字符串類型字段的最大長度

ConcurrencyCheckAttribute:指定用於開放式並發檢查的列的數據類型

TimestampAttribute:將列的數據類型指定為行版本

DatabaseGeneratedAttribute:標記指定實體屬性是由數據庫生成的,並指定生成策略(None數據庫不生成值,Identity當插入行時,數據庫生成值,Computed當插入或更新行時,數據庫生成值)

TableAttribute:指定實體類對應的數據表名

ColumnAttribute:指定實體屬性在數據庫中的列名

ForeignKeyAttribute :指定導航屬性的外鍵字段

NotMappeAttribute:不映射對應字段

映射方式二:Fluent API

Fluent API的配置方式可以將實體類與映射配置進行解耦合

有兩種方式來實現Fluent API的映射配置

  • 第一種:重寫Dbcontext的中OnModelCreating方法

如下面的Book類,不再有Data Annotation特性

public class Book
{
    public int BookID { get; set; }
    public string BookName { get; set; }
    public int Pages { get; set; }
}

重寫Dbcontext中的OnModelCreating方法實現Book類映射的配置:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>().HasKey(t => t.BookID);

    base.OnModelCreating(modelBuilder);
}

現實項目中,實體對象可能是非常多的,在OnModelCreating方法中逐一進行映射配置,可想而知會造成Dbcontext的代碼龐大。

  • 第二種:新建BookMap類,並繼承EntityTypeConfiguration<EntityType>

1、在新建的BookMap類實現映射配置

public class BookMap : EntityTypeConfiguration<Book>
{
    public BookMap()
    {
        this.ToTable("Book", "dbo");
        this.HasKey(p => p.BookID);
        //this.HasKey(p => new { p.BookID, p.BookPreID });//關聯主鍵  
        this.Property(p => p.BookID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);//自動生成
        this.Property(p => p.BookName).IsRequired().HasMaxLength(20).HasColumnName("BookName").IsUnicode(false);//非空,最大長度20,自定義列名,列類型為varchar而非nvarchar
        this.Ignore(p => p.BookDescription);//忽略改屬性的映射
    }
}

2、依舊重寫Dbcontext中的OnModelCreating方法,將BookMap 類的實例添加到modelBuilder的Configurations。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new BookMap());
}

這樣能大大減少OnModelCreating的代碼量,依然存在一個問題,就是實體對象一多,還是要逐條將Map類的實例添加到modelBuilder的Configurations

3、利用反射將程序集中所有的EntityTypeConfiguration添加到modelBuilder.Configurations中,可以說完全解耦了

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
                        .Where(type => !String.IsNullOrEmpty(type.Namespace))
                        .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
    foreach (var type in typesToRegister)
    {
        dynamic configurationInstance = Activator.CreateInstance(type);
        modelBuilder.Configurations.Add(configurationInstance);
    }
}

註:此段代碼源自網友文章,摘自nopCommerce項目的代碼 連接

Entity Framework 查漏補缺 (三)