EF CodeFirst系列(2)---CodeFirst的數據庫初始化

1. CodeFirst的默認約定

1.領域類和數據庫架構的映射約定

  在介紹數據庫的初始化以前咱們須要先了解領域類和數據庫之間映射的一些約定。在CodeFirst模式中,約定指的是根據領域類(如Student,Grade類)自動配置概念模型的一些默認規則。在上一節的小栗子中,咱們沒有在領域類中作任何配置,可是EF API幫咱們配置了主外鍵、關係、列的數據類型等,這就是約定在起做用。下表中列除了一些默認的CodeFirst約定:web

默認規則 描述
Schema EF建立全部的DB對象都放在dbo架構中。dbo.Students
Table Name 實體名的複數,如Student->Students
Foreign key

默認狀況,EF會找和主實體的主鍵名同樣名字的列數據庫

若是沒有的話EF建立一個導航屬性名_導航屬性主鍵形式的外鍵,架構

如:在dbo.Students表中,外鍵是Grade_GradeIdapp

列順序ide

EF建立的數據庫列的數據和領域類屬性的順序一致,惟一可能不一致的是會把主鍵放在第一位函數

映射(mapping)spa

默認EF會把領域類的全部屬性都映射到數據庫,能夠經過[NotMapped]實現領域類/屬性的不映射code

級聯刪除xml

默認啓用全部的類型關係對象

下表顯示了C#數據類型到SqlServer數據類型的映射:

bool bit
byte tinyint
short smallint
int int
long bigint
float real
double float
decimal decimal(18,2)
string nvrchar(Max)
datetime datetime
byte[] varbinary(Max)

下圖顯示了領域類和數據庫架構的映射:

2.一些補充

當咱們使用導航屬性時,EF6中把1對多關係做爲默認關係

注意:EF6中不包含1對1,多對多的默認關係,咱們須要本身經過Fluent API或者註釋屬性進行配置。這些之後會介紹。當EFAPI找不到主鍵時,CodeFist模式會給這個類建立爲複雜類型(Complex Type)。

2.EF肯定數據庫的名字的方式

前邊咱們知道了數據庫中表名,列名,主外鍵名是怎麼來的,可是數據庫的名字是怎麼肯定的呢?下圖展現了數據庫初始化的工做流程,在咱們建立SchoolContext(繼承於DbContext)時,經過給父類的構造函數傳值來肯定數據庫的名字

 經過上圖,上下文類的父構造函數能夠接受以下的參數:

1.無參數

2.數據庫名字

3.鏈接字符串名字

1.無參數

 若是不向父構造函數傳參,EF會在local SQLEXPRESS中建立數據庫,名字是{NameSpace}.{Context Name}。咱們上節的栗子中,建立的數據庫名字就是:EF6Console.SchoolContext

namespace EF6Console
{
    public class SchoolContext : DbContext
    {
        public SchoolContext():base() { }
        public virtual DbSet<Student> Students { get; set; }
        public virtual DbSet<Grade> Grades { get; set; }

    }
}

2.數據庫名字做爲參數

namespace EF6Console
{
    public class SchoolContext : DbContext
    {
        public SchoolContext():base("MySchoolDb") { }
        public virtual DbSet<Student> Students { get; set; }
        public virtual DbSet<Grade> Grades { get; set; }

    }
}

如上邊的代碼所示, 咱們把本身想取的名字(如MySchoolDb)傳入base便可,EF會在local SQLEXPRESS中幫咱們建立一個名字爲MySchoolDb的數據庫。

3.鏈接字符串名字

咱們也能夠經過給base傳入鏈接字符串名字來肯定數據庫名字,傳入鏈接字符串的格式是:"name=yourConnectionString",注意這個是固定格式,若是不加「name=」的話,EF會認爲咱們傳入的是數據庫名字。

namespace EF6Console
{
    public class SchoolContext : DbContext
    {
        public SchoolContext():base("name=SchoolDbConnectionString") { }
        public virtual DbSet<Student> Students { get; set; }
        public virtual DbSet<Grade> Grades { get; set; }
    }
}

App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
    <add name="SchoolDbConnectionString" 
    connectionString="Data Source=.;Initial Catalog=SchoolDB-ByConnectionString;Integrated Security=true" 
    providerName="System.Data.SqlClient"/>
    </connectionStrings>
</configuration>

在上邊的SchoolContext類中咱們把鏈接字符串的名字做爲參數傳入,EF會在app.config或web.config找到鏈接字符串,獲取數據庫的名字(SchoolDB-ByConnectionString),而後使用現有的數據庫,鏈接字符串中的數據庫不存在就在本地Sql Server數據庫中新建一個名爲SchoolDB-ByConnectionString的數據庫(不必定都要是Sql Server,後邊會介紹在MySql中生成數據庫)。

3.數據庫初始化策略

前邊咱們已經在運行程序時生成了數據庫,也知道了數據庫初始化的基本流程,可是有一些新的問題:咱們再次運行程序會建立一個新的數據庫嗎?生產環境怎麼去配置?數據庫生成後若是咱們改變了領域類怎麼辦?爲了解決這些問題咱們必須有一個數據庫遷移策略。

1.四種初始化器

EF中有四種數據庫的初始化器:

1.CreateDatabaseIfNotExists:這是默認的初始化器。這種初始化器在第一次運行程序時會建立數據庫,再次運行不會再建立新的數據庫。可是若是咱們改變了領域類,運行程序時會拋出一個異常,這在前邊的小栗子中已經演示過了。

2.DropCreateDatabaseIfModelChanges:若是領域類發生了改變,刪除之前的數據庫,而後重建一個新的。採用這種初始化器咱們不用再擔憂領域類改變影響數據庫架構的問題。

3.DropCreateDatabaseAlways:使用這種初始化器,咱們每次運行程序都會刪除之前的數據庫,重建新的數據庫。若是在開發過程當中每次都想使用最新的數據庫,那麼能夠採用這種初始化器。

4.Custom DB Initializer:自定義初始化器,若是上邊幾種都不能知足要求,那麼咱們能夠本身定義一個初始化器。

1.經過代碼配置初始化器

使用上邊四種任意一個初始化策略咱們都要使用Database類,以下:

public class SchoolDBContext: DbContext 
{
    public SchoolDBContext(): base("SchoolDBConnectionString") 
    {
        Database.SetInitializer<SchoolDBContext>(new CreateDatabaseIfNotExists<SchoolDBContext>());

        //Database.SetInitializer<SchoolDBContext>(new DropCreateDatabaseIfModelChanges<SchoolDBContext>());
        //Database.SetInitializer<SchoolDBContext>(new DropCreateDatabaseAlways<SchoolDBContext>());
        //Database.SetInitializer<SchoolDBContext>(new SchoolDBInitializer());//自定義初始化器
    }

    public DbSet<Student> Students { get; set; }
    public DbSet<Standard> Standards { get; set; }
}

自定義的初始化器SchoolDBInitializer繼承以上幾種初始化器(這裏繼承CreateDatabaseIfNotExists),以下:

public class SchoolDbInitializer :  CreateDatabaseIfNotExists<SchoolDBContext>
{
    protected override void Seed(SchoolDbContext context)
    {
        base.Seed(context);
    }
}

2.經過配置文件配置初始化器

咱們能夠在配置文件中配置初始化器:

①內置的初始化器

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
    <add key="DatabaseInitializerForType EF6Console.SchoolDBContext, EF6Console"         
        value="System.Data.Entity.DropCreateDatabaseAlways`1[[EF6Console.SchoolDBContext, EF6Console]], EntityFramework" />
    </appSettings>
</configuration>

②自定義初始化器

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>    
    <add key="DatabaseInitializerForType EF6Console.SchoolDBContext, EF6Console"
            value="EF6Console.SchoolDBInitializer, EF6Console" />
    </appSettings>
</configuration>

2.關閉初始化器

① 經過代碼關閉

public class SchoolDBContext: DbContext 
{
    public SchoolDBContext() : base("SchoolDBConnectionString")
    {            
        //關閉初始化器
        Database.SetInitializer<SchoolDBContext>(null);
    }
    public DbSet<Student> Students { get; set; }
    public DbSet<Standard> Standards { get; set; }
}

 ② 經過配置文件關閉

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>    
    <add key="DatabaseInitializerForType EF6Console.SchoolDBContext,EF6Console"
            value="Disabled" />
    </appSettings>
</configuration>
相關文章
相關標籤/搜索