在開發專案時很多時候若有連接到資料庫,通常會把 Model 這個部分先切割出來開發,這樣可以讓 Model 獨立在主 project 之外讓其他 projects 可以一起使用,在維護的時候其實也會比較方便只需要抽換DLL就好(這也算關注點分離嗎),另外就是 Model 天生就比較能夠獨立出來開發。不過在獨立開發 Model 的時候也要注意一些小東西。今天使用 Entity Framework (version 5) 的 Code First with existing database 的方式來做個示範。

實作部分

我們選用的 Database 是赫赫有名的 Northwind 資料庫,我想這個資料庫大家應該都比我還熟了。這次我們選出 Products 和 Categories 這兩個資料表來做示範的 Table 吧~!順便提醒一下用 Code First with existing database 時會有些小地方需要注意! 首先~我們就來把 Northwind database bring online 吧~

Northwind

把資料庫 online 後,接下來就切到 Visual Studio 中創一個新的 Class Library 專案吧~ 專案名稱就隨便給個 Project.Model 來代表這個 DLL 是我們的 Model。而因為要用 Entity Framework 的 Code First 功能,我們要引用一個 EF 的 DLL:EntityFramework,若是用 Nuget 來安裝的話會連同 System.Data.Entity 這個 DLL 一起裝,不過若是沒有要用視覺設計工具來輔助的話其實是可以不用這個 DLL 的。

使用 Code First 的方式來與資料庫做互動的話需要撰寫一個繼承 DbContext 的類別。這裡我們就把這個類別名稱取為 Northwind。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
using System.Data.Entity;
///這邊我們只用到Products與Categories這兩個資料表做示範。
///DbSet這個類別代表一組在Context中的實體資料集合,但要注意這個集合內的型別(class)都是相同的,
///不能接受複合型別,像是DbSet<Product,Category>(這邊型別相當於資料庫中的資料表的概念)。
public class Northwind : DbContext
{
    // 這裡的屬性名稱要注意,需要與資料表的名稱相同!
    // 2012-11-16更正!
    // 這邊要注意的應該是DbSet裡的名稱,(也就是Product與Catagory)
    // Case 1:沒有貼Table標籤
    // Code-First預設會依據DbContext中DbSet的泛型名稱(這裡就是Products, Catagories)作為
    // 資料庫中對應的表格名稱。若資料庫中沒有這個表格Code First會幫你產生(很貼心吧,千萬小心)。
    // Case 2:Product有貼Table標籤
    // Code-First會依據標籤所給予的名稱來做為對應到資料庫中的表格名稱。
    public DbSet<Product> Products { get; set; }

    public DbSet<Category> Categories { get; set; }
}

而另外兩個資料表則如下撰寫:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using System.ComponentModel.DataAnnotations.Schema;
public class Product
{
    // Code-First 的預設欄位對應會自動把ID結尾的屬性辨識為PrimaryKey,但可以用[Key]標籤來輔助。
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public Decimal? UnitPrice { get; set; }
    public bool Discontinued { get; set; }
    /// 注意!使用 Code First 連接既有資料庫沒有加上這個 Schema 標籤會出錯誤!
    /// 因為使用Category與Product資料表,因此需要將這兩張表格的關係寫明。
    /// 若是只引用Product資料表則不用寫也沒關係。
    /// 當然若兩張表格彼此沒有直接關係的話也不用寫這個。
    [ForeignKey("Category")]
    public int CategoryID { get; set; }

    public virtual Category Category { get; set; }
}

另外一張資料表(Category)如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
using System.Collections.Generic;
public class Category
{
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    public string Description { get; set; }
    public byte[] Picture { get; set; }

    public virtual ICollection<product> Products { get; set; }
}

另外我們撰寫另外一個類別來使用Northwind至這個類別(或是說資料庫)~

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class NorthwindRepository : IDisposable
{
    private Northwind _db;
    //這裡的資料存取為求簡單沒有做依賴注入的動作。
    public NorthwindRepository()
    {
        _db = new Northwind();
    }

    public ICollection<string> ListAllProductName()
    {
        return _db.Products.Select(a => a.ProductName).ToList();
    }

    public ICollection<string> ListAllCategoryName()
    {
        return _db.Categories.Select(a => a.CategoryName).ToList();
    }

    public void Dispose()
    {
        if (_db != null)
            _db.Dispose();
    }
}

接下來就是要注意的就是使用 Code First with existing database 時並不會自動幫我們把連線字串準備好,這必須得自行撰寫。因此我們在 App.config (這個檔案會在安裝 Entity Framework 的時候幫我們把基本的東西宣告好)中,需要加入一段 connectionStrings 標籤~

1
2
3
<add name="Northwind"
         connectionString="Data Source=your source;Initial Catalog=Northwind;User Id=uid;Password=pwd" 
         providerName="System.Data.SqlClient"/>

若不確定連線字串如何撰寫可以參考這個網頁,裡面有很多資料庫的連線字串撰寫方法。 以上都寫好了之後其實就可以來使用我們的 Northwind 資料庫了~這邊我們新創一個空的 web form 專案,首先我們要先把我們的連線字串放置 web.config 中,然後就是引用 Project.Model.dll 了~!不過這邊要注意,引用的 dll 不能把 copy local 設為 false,否則在 create 物件時會出現錯誤! 另外使用於 console 專案時,也可以直接把 App.config 複製過去,一樣參考 DLL 時 copy local 設為 true 就可以直接拿來用了!像是這樣~

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
using Project.Model;
static void Main(string[] args)
{
    // 能用using時儘量用,至少沒有害處。
    using (NorthwindRepository db = new NorthwindRepository())
    {
       foreach (var item in db.ListAllProductName())
       {
            Console.WriteLine("item name: {0}", item);
       }
    }
    Console.WriteLine("\nFinish!");
    Console.Read();
}