微軟WP7本機數據庫解決方案之SQLite
無論是從用戶的角度來看還是從開發人員的角度來看,Windows Phone 7給我們帶來眾多的新的和令人振奮的功能。與此同時,當前的Windows Phone 7系列也的確存在令許多用戶失望的缺憾。一個代表性的遺憾便是,Windows Phone 7缺乏本地數據庫API支持—當前的Windows Phone 7操作系統僅提供通過XML、客戶端獨立存儲和云存儲等幾種有限的數據訪問支持。那么,就本地數據庫功能來說,我們真的有沒有其他的選擇了嗎?當然不是這樣。如今各種開源社團如火如荼,已經有一些開發人員和團隊一直努力在填補這一方面的空白。
請注意,盡管目前已有多個嘗試實現了Windows Phone 7本地數據庫支持,但***,這些系統都需要運行于Windows Phone 7提供的獨立存儲基礎之上。在本系列文章中,我將向你介紹上述成員之一-Sqlite Client for Windows Phone。這是一種新的,功能強大且易于使用的Windows Phone 7本機數據庫解決方案,該系統公布于著名的開源網站CodePlex。篇幅所限,我僅介紹在Windows Phone 7系統中使用Sqlite Client for Windows Phone編程的部分技巧。
[備注]本系列文章中的案例程序調試環境包括:
- Windows 7;
- .NET 4.0;
- Visual Studio 2010;
- Windows Phone Developer Tools RTW;
- Sqlite Client for Windows Phone (http://sqlitewindowsphone.codeplex.com/);
- (推薦) sqlite-manager (http://code.google.com/p/sqlite-manager/);
- (推薦) MVVM Light Toolkit (http://mvvmlight.codeplex.com/).
一、Sqlite Client for Windows Phone簡介
大家都知道,SQLite是一個著名的開源的嵌入式的數據庫系統,目前已經提供對于iOS和Android的良好支持。在此,應當讓我們感謝Dan Ciprian Ardelean,是他帶給我們WP7版本的SQLite-C#-SQLite!最近幾個月,作者對早期的版本又進行了更新,得到一個功能更強大和更容易使用的解決方案,改名為Sqlite Client for Windows Phone,在知名的開源網站CodePlex上發布,網址是http://sqlitewindowsphone.codeplex.com/releases。
▲圖1. 下載Sqlite Client for Windows Phone的界面截圖
Sqlite Client for Windows Phone進行大范圍的更新,如提供對于布爾類型、大型數據(Blobs)以及事務的全面支持,此外,下載包中還一并提供了一個全面的示例,供開發者學習之用。
▲圖2. Sqlite Client for Windows Phone源碼工程及示例工程架構
接下來的操作就很簡單了:重新構建源碼工程,得到一個程序集Community.CsharpSqlite.WP.dll(Release版本大小是525 KB)。然后,在你的WP7 Silverlight 項目中添加對該程序集的引用。***,你便可以使用Sqlite Client for Windows Phone提供的本地數據庫支持功能了。
#p#
二、Sqlite Client for Windows Phone基礎類庫剖析
如果您以前有過任何基于SQL腳本的數據庫編程經驗,那么您可以輕松地使用Sqlite Client for Windows Phone功能。特別值得一提的是,此工程建基于以前的C#-SQLite項目之一,通過引入幾個幫助器類(在文件SQLiteClient.cs),即SQLiteException、SQLiteConnection和SQLiteCommand,進一步簡化了基本的數據庫和表相關操作。接下來的幾幅圖展示了Sqlite Client for Windows Phone中提供的主要組件及其之間的關聯關系。
▲圖3. Sqlite Client for Windows Phone最頂層組件
▲圖4. SQLiteConnection類中的主要組件
▲圖5. SQLiteCommand類中的主要組件
怎么樣!如果您以前熟悉任何基于SQL的數據庫開發,相信上面的這些組件對您會非常親切吧。
先別急,在正式使用Sqlite Client for Windows Phone之前,有必要再向您介紹另外一個非常有用的工具,名為sqlite-manager (http://code.google.com/p/sqlite-manager/)。這個工具是以FireFox插件的方式提供的。到現在您應該明白了,絕大多數與SQLite數據庫相關的操作,例如創建SQLite數據庫、表、視圖、索引等等,都可以通過sqlite-manager輕松完成。
1.使用SQLite Manager簡化數據庫管理
如前所述,SQLite Manager是一個Firefox插件,使用Firefox的加載項管理器你可以很容易地獲取和安裝這個控件(圖6)。
▲圖6. 使用Firefox的加載項管理器獲取和安裝SQLite Manager插件
如圖所示,如果你打開Firefox的插件管理器,然后輸入“SQLite Manager”搜索文本,你會很容易檢索到此加載項。然后,您可以點擊按鈕“添加到Firefox... ”開始下載并安裝SQLite Manager。請注意,在Firefox的提示后,你應該重新啟動Firefox以完成安裝。
使用SQLite Manager是容易的。說實在的,這是我***次使用SQLite Manager,我發現這個工具功能強大而且極易上手。如果您使用過VB6中,你可能熟悉其中內置的數據庫管理工具-VisData(以便以內置方式創建小型的Access數據庫)。說實話,VisData確實是不容易使用,但在當時我們覺得已經相當不錯了。現在,你只須記住,SQLite Manager的功能要比VisData強大1000倍。下圖展示了SQLite Manager的一個使用快照。
▲圖7. SQLite Manager使用快照
從圖中可見,你可以使用SQLite Manager來實現幾種各種SQLite相關操作。請注意,要想了解更多的有關于SQLite的概念及詳細使用語法,請從這款插件的幫助菜單中尋找答案。
另外,你也會注意到,在本文簡單的示例工程中,我僅創建了一個表格Customer,對應的數據庫文件為database1.sqlite(如圖8所示)。
▲圖8. 使用SQLite Manager定義表格Customer字段
在創建完數據庫database1.sqlite后,關閉SQLite Manager插件。然后,把此文件復制或移動到對應示例工程WP7SQLiteClient的根目錄下。請注意,接下來,把它的Build Action屬性設置為Resource模式。設置成這種模式的原因與接下來的操作方式相關。如果選擇Content模式,則在后臺的示例工程中你需要修改對應的部分源碼。
2.一個有用的工具類-DBHelper
如上所述,Sqlite Client for Windows Phone使用眾所周知的SQL操作針對典型的數據庫操作提供了一個高層次的封裝。因此,在Silverlight for Windows Phone 7編程中為了處理SQLite數據庫操作,我們可以直接使用在文件SQLiteClient.cs中定義的對象(在源庫項目),即SQLiteException、SQLiteConnection和SQLiteCommand等。
雖然Sqlite Client for Windows Phone并沒有提供與獨立存儲的直接互動,但顯然增加對獨立存儲支持是必要的,這樣可以改善系統的性能。因此,我們可以進一步封裝前面提到的SQLiteClient對象。為此,Chris開發了一個非常好用的實用工具類,叫做DBHelper。為了應用于我們自己的示例,我對它做了輕微的修改。完整的源碼如下。
#p#
列表1:更新版本的工具類DBHelper
- //others omitted…
- using SQLiteClient;
- using System.Linq;
- using System.IO.IsolatedStorage;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- namespace WP7SQLiteClient.Helpers
- {
- public class DBHelper
- {
- private String _dbName;
- private SQLiteConnection db = null;
- public DBHelper(String assemblyName, String dbName)
- {
- IsolatedStorageFile store =IsolatedStorageFile.GetUserStoreForApplication();
- if (!store.FileExists(dbName))
- {
- CopyFromContentToStorage(assemblyName, dbName);
- }
- _dbName = dbName;
- }
- ~DBHelper()
- {
- Close();
- }
- private void Open()
- {
- if (db == null)
- {
- db = new SQLiteConnection(_dbName);
- db.Open();
- }
- }
- private void Close()
- {
- if (db != null)
- {
- db.Dispose();
- db = null;
- }
- }
- //Insert operation
- public int Insert(T obj, string statement) where T : new()
- {
- try
- {
- Open();
- SQLiteCommand cmd = db.CreateCommand(statement);
- int rec = cmd.ExecuteNonQuery(obj);
- return rec;
- }
- catch (SQLiteException ex)
- {
- System.Diagnostics.Debug.WriteLine("Insert failed: " + ex.Message);
- throw ex;
- }
- }
- // Delete operation
- public void Delete(string statement) where T : new()
- {
- try
- {
- Open();
- SQLiteCommand cmd = db.CreateCommand(statement);
- cmd.ExecuteNonQuery();
- }
- catch (SQLiteException ex)
- {
- System.Diagnostics.Debug.WriteLine("Deletion failed: " + ex.Message);
- throw ex;
- }
- }
- //Query operation
- public List SelectList(String statement) where T : new()
- {
- Open();
- SQLiteCommand cmd = db.CreateCommand(statement);
- var lst = cmd.ExecuteQuery();
- return lst.ToList();
- }
- public ObservableCollection SelectObservableCollection(String statement)
- where T : new()
- {
- List lst = SelectList(statement);
- ObservableCollection oc = new ObservableCollection();
- foreach (T item in lst)
- {
- oc.Add(item);
- }
- return oc;
- }
- private void CopyFromContentToStorage(String assemblyName,String dbName)
- {
- IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
- System.IO.Stream src = Application.GetResourceStream(
- new Uri("/" + assemblyName + ";component/" + dbName,UriKind.Relative)).Stream;
- IsolatedStorageFileStream dest = new IsolatedStorageFileStream(dbName,
- System.IO.FileMode.OpenOrCreate,
- System.IO.FileAccess.Write, store);
- src.Position = 0;
- CopyStream(src, dest);
- dest.Flush();
- dest.Close();
- src.Close();
- dest.Dispose();
- }
- private static void CopyStream(System.IO.Stream input,IsolatedStorageFileStream output)
- {
- byte[] buffer = new byte[32768];
- long TempPos = input.Position;
- int readCount;
- do
- {
- readCount = input.Read(buffer, 0, buffer.Length);
- if (readCount > 0)
- {
- output.Write(buffer, 0, readCount);
- }
- } while (readCount > 0);
- input.Position = TempPos;
- }
- }
- }
順便說一句,對于上面這個幫助類我也沒有提供細致的優化編碼。希望讀者根據您的相關工作能夠繼續進行這項工作(例如提供更好的泛型化的CRUD支持)并分享給廣大網友。簡言之,我主要是增加了插入和刪除方法。上面的代碼中最引起您注意是地方一定是方法CopyFromContentToStorage,正是借助這個方法我們實現了上述目標-建立起SQLite數據庫與獨立存儲的關系。
三、小結
本文中簡要介紹了Sqlite Client for Windows Phone的主要功能及相關的輔助開發工具。在接下來的第二篇文章中,我們將具體構建一個簡單的Windows Phone 7客戶端應用程序,當然要涉及到Sqlite Client for Windows Phone的基本編程技巧。
【編輯推薦】