WP7 - Local Database概論 - 1

Windows Phone 7 - Local Database概論 - 1

去年剛在WP7被釋放出來之後,我在研究WP7上使用資料庫的文章後,有產出<Windows Phone 7 – 下載檔案至Isolated Storage>文章,

當時是使用Isolated Stroage結合SQLite Library產生新的資料庫(或用下載)放置於Isolated Stroage進行使用。等到今天,終於在Mango

發出API項目資訊後,可以開始研究由MS所提供的API,使用LINQ to SQL操作Local Database(其實相似SQL CE)。

 

WP 7.1開始允許儲存關聯(relational)資料在local database中,local database放置的位置不意外被放置於Isolated Storage,

放置放Isolated Storage操作起來不困難,但這跟以前操作SQL CE資料庫是比較不一樣的,因為它統一使用LINQ to SQL完成所有動作

包括:建立Schema、操作資料、存取實際的database file。

這樣的使用方法不是沒有道理的,LINQ to SQL協助我們把底層資料與操作層分開來,透過IEnumable<T>實作的類別與物件導向的設計概念

配合Lambda語言,讓我們操作起來不需要去在意實際的SQL或資料結構,只要注意Lambda不要找出null大部分的任務都可以完成。

對於LINQ To SQL比較不熟悉的話,可以參考<[超簡單入門]VS 2008 + LINQ-to-SQL,不用寫程式,一次搞定。>寫的我覺得很容易上手。

 

以下將仔細說明LINQ To SQL在WP7怎用操作Local Database:

〉LINQ To SQL in WP7

根據MSDN提供的文件,Linq to SQL in WP7中的架構,可以透下圖來加以說明:

IC505326

從上圖可以看出我們撰寫的App裡需要為想操作的Local Database,建立一個對應的DataContext物件讓操作邏輯可以直接編寫於該物件

上面,不需要去在意底層怎麼轉成可能SQL或組合語言,將這些轉換與對應的事情交給LINQ to SQL Runtime去負責即可。

但這只是大略的說明,接下來針對幾個重要的元件做一些說明:

 

(A) DataContext

Linq to SQL操作時有一個重要的物件:DataContext,它以object model存在,用於在代表Database(但不是直接存取Database),

以一個中間層的方式存在著。簡單來說,它內部包括了所有Database裡的Table、Row、Column與Schema資訊,舉例來說:

「每一個Table都會轉有一個對應的Entity,該Entity裡內含有Table的Columns(轉成屬性)、關聯性等資訊,開發時則直接操作這些Entity」。

這也是ORM概念實作的方式,例如:Entity Framework或LINQ to SQL。

然而,在WP7裡採用System.Data.Linq.DataContext物件建立而成,它也就是上述的中間層,隔離底層資料與操作層的重要角色。

所以今天你要在WP7操作Local Database,第一步要做的就是定義DataContext。

 

(B) LINQ to SQL runtime

LINQ to SQL提供一個物件關係(Object-Relational)的對應特性,讓我們可以透過LINQ語言去操作資料,然而Runtime的部分,

主要是將操作的邏輯(LINQ語法)轉換成實際操作資料的方法(例如:T-SQL),至於回傳的結果,呈現以集合的方式讓接收端進行操作。

在這要特別注意的是:[LINQ to SQL Support for Windows Phone]

(b-1). LINQ to SQL在WP7上不支援直接執行T-SQL、DDL(Data Definition Language)或DML(Data Modeling Language)。

(b-2). LINQ to SQL在WP7也不支援直接操作ADO.NET Object

 

 

(C) 存取Isolated Storage

LINQ to SQL操作的對象,其實是一個實際的Database檔,並且採用Isolated Storage存取這份實體檔案。

在WP7裡操作Database檔:以*.sdf檔為命名。不過操作方式跟操作Isolated Storage的寫法太一致,過去使用「IsolatedStorageFile」

操作Isolated Stroage的資料,比較接近使用檔案結構的觀念。那換到LINQ to SQL呢?如下程式碼:

   1: //需要自訂一個DataContext做為操作對象
   2: public class DBHandler : DataContext
   3: {
   4:     //指定連結字串:以isostore為關鍵字
   5:     public static string DBConnection = "Data Source=isostore:/ToDo.sdf";
   6:  
   7:     //繼承DataContext
   8:     public DBHandler(string pDBConnection) : base(pDBConnection) { }
   9:  
  10:     //定義Table的項目
  11:     public Table<Contacts> gContacts;
  12: }
  13:  
  14: [Table]
  15: public class Contacts : INotifyPropertyChanged, INotifyPropertyChanging
  16: {
  17:     private int gSerail = 0;
  18:     [Column(IsPrimaryKey = true, 
  19:         IsDbGenerated = true, 
  20:         DbType = "INT NOT NULL Identity", 
  21:         CanBeNull = false, AutoSync = AutoSync.OnInsert)]
  22:     public int Serial
  23:     {
  24:         get { return gSerail; }
  25:         set {
  26:             //NotifyPropertyChanging("Serial");
  27:             gSerail = value;
  28:            // NotifyPropertyChanged("Serial");
  29:         }
  30:     }
  31:  
  32:     public event PropertyChangedEventHandler PropertyChanged;
  33:  
  34:     public event PropertyChangingEventHandler PropertyChanging;
  35: }

如上述程式碼:「Data Source=isostore:/xxx.sdf」,指定的實體檔案位置。注意這裡使用的路徑,另外,

更詳細的可以參考<Local Database Connection Strings for Windows Phone>,裡面包括了支援加密檔案大小、存取模式、文化類型

(與sort、search資料時相關)等。對於local database也可以使用Isolated Storage取出來。

 

如下程式碼:

   1: using (IsolatedStorageFile tStorage = IsolatedStorageFile.GetUserStoreForApplication())
   2: {
   3:     String tFileList = string.Empty;
   4:     //搜尋*.sdf指定的檔案
   5:     foreach (string tFileName in tStorage.GetFileNames("*.sdf"))
   6:     {
   7:         tFileList += string.Format("File Name:{0} \r\n", tFileName);
   8:     }
   9:     tblFileList.Text = tFileList;
  10: }

 擷取 可以看到建立出來的sdf檔,透過Isolated Storage也可以取得到。但需透過DataContext來溝通。

 

(D) 操作Local Database主要四個API

(D-1) System.Data.Linq

        主要包括與Location Database互動的方法與操作項目,它於Microsoft.Phone.Data.Linq差異在於,

        該Namespace提供比較實作操作時針對資料型別、泛型資料的運用。

(D-2) System.Data.Linq.Mapping

        主要用於定義與產生Location Database,與Microsoft.Phone.Data.Linq.Mapping下的類別差異在於:

        該Namespace下的類別包括提供Column、MappingSource與Model的操作。

(D-3) Microsoft.Phone.Data.Linq

        主要操作Local Database的Schema Update與LINQ to SQL API的延伸。

(D-4) Microsoft.Phone.Data.Linq.Mapping

        主要用於定義與產生Local Database實體檔,存取Data Context。透過Index來操作指定的DB或Table。

以上四個API透過簡單的定義,相信一定沒有很清楚,但比較常用到的都是在System.Data.Linq.*該命名空間下的類別,

主要原因是操作資料的任務比較多都是在這些類別中完成。

 

大致說明有關Local Database使用的主要類別與觀念之後,接下來繼續說明,如何定義Data Context與操作它。

〉定義Data Context

進行WP7操作Local Database第一步:實作Data Context。仔細說明該Local Database會有那些Table、Table Schema、

Table之間的關聯…等。在定義Table與Schema時,可以使用<Attribute-Based Mapping (LINQ to SQL) >這篇文章了解每

一個保留字的函意。以下簡單舉個例子:

(1) 定義Table與Table Schema

   1: //實作INotifyPropertyChanged與INotifyPropertyChanging介面,做為屬性(欄位)值變更時的事件觸發。
   2: [Table]
   3: public class Contacts : INotifyPropertyChanged, INotifyPropertyChanging
   4: {
   5:     //該Contacts的Table具有三個欄位:ID、Name、PhoneNumber
   6:     private int gID;
   7:     [Column(IsPrimaryKey = true, IsDbGenerated = true,
   8:             DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
   9:     public int ID
  10:     {
  11:         get
  12:         {
  13:             return gID;
  14:         }
  15:         set
  16:         {
  17:             NotifyPropertyChanging("ID");
  18:             gID = value;
  19:             NotifyPropertyChanged("ID");
  20:         }
  21:     }
  22:  
  23:     private string gName;
  24:     [Column(IsPrimaryKey = false, DbType = "VARCHAR(10)", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
  25:     public string Name
  26:     {
  27:         get
  28:         {
  29:             return gName;
  30:         }
  31:         set
  32:         {
  33:             NotifyPropertyChanging("Name");
  34:             gName = value;
  35:             NotifyPropertyChanging("Name");
  36:         }
  37:     }
  38:  
  39:     private string gPhoneNumber;
  40:     [Column(DbType = "VARCHAR(20)", CanBeNull = true, AutoSync = AutoSync.OnInsert)]
  41:     public string PhoneNumber
  42:     {
  43:         get { return gPhoneNumber; }
  44:         set
  45:         {
  46:             NotifyPropertyChanging("PhoneNumber");
  47:             gPhoneNumber = value;
  48:             NotifyPropertyChanged("PhoneNumber");
  49:         }
  50:     }
  51:  
  52:     #region INotifyPropertyChanging觸發事件
  53:     public event PropertyChangingEventHandler PropertyChanging;
  54:  
  55:     private void NotifyPropertyChanging(string pPropertyName){
  56:         if (PropertyChanging!=null){
  57:             PropertyChanging(this, new PropertyChangingEventArgs(pPropertyName));
  58:         }
  59:     }
  60:     #endregion
  61:  
  62:     #region INotifyPropertyChanged觸發事件
  63:     public event PropertyChangedEventHandler PropertyChanged;
  64:  
  65:     private void NotifyPropertyChanged(string pPropertyName)
  66:     {
  67:         if (PropertyChanged != null)
  68:         {
  69:             PropertyChanged(this, new PropertyChangedEventArgs(pPropertyName));
  70:         }
  71:     }
  72:     #endregion
  73: }

定義一個名叫Contacts的資料表,並且具有三個欄位(屬性):ID、Name與PhoneNumber。其中該Tablen實作了二個重要的介面:

INotifyPropertyChangingINotifyPropertyChanged,實作介面用途在於當操作該Contacts物件時,可以補捉屬性值被更動的通知

定義好了Table元素之後,接下來繼承DataContext類別,並且提供指定的DBConnection(包括實體*.sdf的產生路徑)

   1: public class DBHandler : DataContext
   2: {
   3:     public static string DBConnection = "Data Source=isostore:/ToDo.sdf";
   4:  
   5:     public DBHandler(string pDBConnection) : base(pDBConnection) { }
   6:  
   7:     public Table<Contacts> gContacts;
   8: }

 

(2) 建立Local Database

   1: //建立*sdf的local database檔案。
   2: using (DBHandler tDB = new DBHandler (DBHandler .DBConnectionString))
   3: {
   4:     if (tDB.DatabaseExists() == false)
   5:     {
   6:         //如果不存在,即建立。
   7:         tDB.CreateDatabase();
   8:     }
   9: }

定義好了Table、DataContext,接下來就是將繼承DataContext的類別加以實例化,這裡要注意的地方於在:DataContext針對local database

的存取機制,要記得DataContext只是一個中間層,也就代表當實例化一個DataContext類別,即等於連線於local database的一個連線。

為了在整個Application中其他操作時,能夠保持一個連線與一致的資料,建議可以獨立成一個靜態物件來操進

 

[補充]

〉WP7的Local Database與SQL Server(或SQL CE)的差異:

由於SQL Server是屬於PC/Server上的應用,具有豐富的硬體資源可以支援,但在WP7裡資源有限,所以針對DB的操作也有所限制,

主要有幾個重要的差異:

a. local database主要執行於應用程式的執行緒。不像Client-Server架構,也不支援背景服務的使用。

b. local database只能被相關的應用程式所執行,因為它放於Isolated Storage,各App之間是獨立的)。

c. local database只能透過LINQ to SQL進行操作

 

======

以上是針對WP7.1開始提供local database的操作,做一個簡單的概略介紹,其實針對local database的操作,

在建立好整個*.sdf檔與Data Context之後,接下來的部分即可以使用LINQ to SQL的寫法來進行資料的存取。

 

References:

Local Database Overview for Windows Phone

Attribute-Based Mapping (LINQ to SQL) (定義DataContext必讀)

[超簡單入門]VS 2008 + LINQ-to-SQL,不用寫程式,一次搞定。

使用者入門 (LINQ to SQL)

Step-By-Step教學(1):改寫LINQ to SQL資料異動行為

為什麼需要LINQ to SQL ? 關於 SQL 的 Like 子句

What's New in Windows Phone Developer Tools

Code Samples for Windows Phone (學習範例與相關說明) & Mango後的課程

Local Data Storage for Windows Phone

Reference (LINQ to SQL) (豐富的資源) & 101 LINQ Samples (學習LINQ的好地方)