天天看點

Entity Framework-Code First to a New Database

文獻參考

MSDN EF架構官方文檔

此文章完全根據微軟官方文檔資料,整個過程我已經在visual studio 中走了一遍,在這裡将其總結下來,為以後複習所用,也為需要入門 Entity Framework 的朋友作為一個參考資料。力求準确無誤。

建立模型(Model)

public class Blog 
{ 
    public int BlogId { get; set; } //這是EF預設的主鍵名稱,ClassName+id,以便EF架構自動辨識
    public string Name { get; set; } 

    public virtual List<Post> Posts { get; set; } //這是一個導航屬性,注意virtual的用法
} 

public class Post 
{ 
    public int PostId { get; set; } 
    public string Title { get; set; } 
    public string Content { get; set; } 

    public int BlogId { get; set; } //這個是外鍵,BlogId這個名稱是标準的命名方法,需要注意
    public virtual Blog Blog { get; set; } //這是一個導航屬性,注意virtual的用法
}
           

說明:

1. 導航屬性分别為

Blog.Posts and Post.Blog

2. 通過使導航屬性

virtual

化,確定了EF架構的延遲加載特征( Lazy Loading

feature),延遲加載意味着當通路它們時,這些屬性的内容将會被自動地從資料庫中加載出來。

建立上下文(context)

現在是時候定義一個派生的上下文了,它代表着同資料庫(database)的一次會話(session),允許我們去查詢和儲存資料。我們定義一個上下文,它派生于

System.Data.Entity.DbContext

,暴露出一個泛型成員

DbSet<TEntity>

。因為要用到EF架構的類型,是以我們需要添加

EntityFramework NuGet package

,添加的方法見位址: https://msdn.microsoft.com/en-us/library/jj193542(v=vs.113).aspx,第3節。

添加命名空間

using System.Data.Entity;

,定義下面的派生類:

public class BloggingContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
}           

完整的代碼如下所示:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Data.Entity; //DbContext派生類引用

namespace CodeFirstNewDatabaseSample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
        } 
    } 

    public class Blog 
    { 
        public int BlogId { get; set; } 
        public string Name { get; set; } 

        public virtual List<Post> Posts { get; set; } 
    } 

    public class Post 
    { 
        public int PostId { get; set; } 
        public string Title { get; set; } 
        public string Content { get; set; } 

        public int BlogId { get; set; } 
        public virtual Blog Blog { get; set; } 
    } 

    public class BloggingContext : DbContext 
    { 
        public DbSet<Blog> Blogs { get; set; } 
        public DbSet<Post> Posts { get; set; } 
    } 
}
           

讀寫資料

實作Main 方法。這個代碼建立了一個我們定義的上下文的一個執行個體,然後用它插入一條 Blog 執行個體。然後用 LINQ 查詢,從資料庫中追溯所有的Blog執行個體,并按照題目的字母順序排序。

class Program 
{ 
    static void Main(string[] args) 
    { 
        using (var db = new BloggingContext()) 
        { 
            // Create and save a new Blog 
            Console.Write("Enter a name for a new Blog: "); 
            var name = Console.ReadLine(); 

            var blog = new Blog { Name = name }; 
            db.Blogs.Add(blog); 
            db.SaveChanges(); 

            // Display all Blogs from the database 
            var query = from b in db.Blogs 
                        orderby b.Name 
                        select b; 

            Console.WriteLine("All blogs in the database:"); 
            foreach (var item in query) 
            { 
                Console.WriteLine(item.Name); 
            } 

            Console.WriteLine("Press any key to exit..."); 
            Console.ReadKey(); 
        } 
    } 
}
           

在運作程式前,需要配置App.config檔案中的資料庫連結,我自己運作時,是現在資料庫中建立一個EFDemoDb資料庫,否則運作程式時,在DbSet.Add處總是提示沒有在master資料庫建立新資料庫權限。這樣按照要求配置好資料庫的連結後,運作完畢後,在資料庫中增加了兩張表。注意觀察。

<connectionStrings>
    <add name="BloggingContext" 
         connectionString="Data Source=(local); Database=EFDemoDb; 
         User ID=sa; Password=sa;" providerName="System.Data.SqlClient"/>
  </connectionStrings>           

處理資料模型的變化

改到我們對我們的模型做一些變化的時候了,當我們在程式Model中變化時,我們也需要更新資料庫的架構(schema)。為了做這些,我們要用 Code First Migrations,簡稱 Migrations。

Migrations 允許我們經過若幹步驟來更新(upgrade)或還原(downgrade)我們的資料庫架構。這些步驟中的每一步,稱作 一次遷移(Migration),包含一些代碼來描述将要應用的變化。

這個工具的位置在 工具->Package Manager Console。運作它,然後輸入 Enable-Migrations,此時會自動增加一個 Migrations的檔案夾,包含了2項,

Configuration.cs – This file contains the settings that Migrations will use for migrating BloggingContext. We don’t need to change anything for this walkthrough, but here is where you can specify seed data, register providers for other databases, changes the namespace that migrations are generated in etc.

*_InitialCreate.cs – This is your first migration, it represents the changes that have already been applied to the database to take it from being an empty database to one that includes the Blogs and Posts tables. Although we let Code First automatically create these tables for us, now that we have opted in to Migrations they have been converted into a Migration. Code First has also recorded in our local database that this Migration has already been applied. The timestamp on the filename is used for ordering purposes.

Entity Framework-Code First to a New Database

現在我們對模型做一個改變,為Blog模型增加一個 Url 屬性。在 Package Manager Console中運作 Add-Migration AddUrl 指令,在Migrations檔案夾下自動增加 201705140211306_AddUrl.cs檔案,然後補充如下所示:

namespace CodeFirstNewDatabaseSample.Migrations 
{ 
    using System; 
    using System.Data.Entity.Migrations; 

    public partial class AddUrl : DbMigration 
    { 
        public override void Up() 
        { 
            AddColumn("dbo.Blogs", "Url", c => c.String()); 
        } 

        public override void Down() 
        { 
            DropColumn("dbo.Blogs", "Url"); 
        } 
    } 
}           

然後,運作 Update-Database 指令。這個指令将應用任何即将的遷移到資料庫中。

Tip: You can use the –Verbose switch when calling Update-Database to see the SQL that is being executed against the database.

資料注釋(Data Annotations)

至今我們已經讓EF發現了用它的預設值去探索了模型,但是有時候我們的類并沒有遵守這些預設值,我們需要進一步的配置。有兩種方法可以做到:我們看一看資料注釋(Data Annotations)和 fluent API。

讓我們增加一個User模型,

public class User 
{ 
    public string Username { get; set; } 
    public string DisplayName { get; set; } 
}
           

在DBContext中增加,

public class BloggingContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<User> Users { get; set; } //users 
}
           

添加

using System.ComponentModel.DataAnnotations;

引用,通過Data Annotations 對 User添加主鍵:

public class User 
{ 
    [Key] 
    public string Username { get; set; } 
    public string DisplayName { get; set; } 
}           

完整的Data Annotations包括:(詳細見 https://msdn.microsoft.com/en-us/library/jj193542(v=vs.113).aspx 第6部分)

KeyAttribute

StringLengthAttribute

MaxLengthAttribute

ConcurrencyCheckAttribute

RequiredAttribute

TimestampAttribute

ComplexTypeAttribute

ColumnAttribute

TableAttribute

InversePropertyAttribute

ForeignKeyAttribute

DatabaseGeneratedAttribute

NotMappedAttribute

Fluent API

The fluent API is a more advanced way of specifying model configuration that covers everything that data annotations can do in addition to some more advanced configuration not possible with data annotations. Data annotations and the fluent API can be used together.

覆寫 BloggingContext .OnModelCreating 方法。

public class BloggingContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<User> Users { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
        modelBuilder.Entity<User>() 
            .Property(u => u.DisplayName) 
            .HasColumnName("display_name"); 
    } 
}
           

啟用 Add-Migration ChangeDisplayName 指令,運作Update-Database 指令遷移到資料庫。