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中的架構,可以透下圖來加以說明:
從上圖可以看出我們撰寫的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實作了二個重要的介面:
INotifyPropertyChanging與INotifyPropertyChanged,實作介面用途在於當操作該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,不用寫程式,一次搞定。
‧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的好地方)