前言
.NET Micro Framework系統官方代碼是不支援任何資料庫的,這對一些具有使用者管理的Web Server、RFID資料擷取和複雜的手持機應用來說是非常不方便的。
很早就知道了SQLite,但是一直沒有深入研究,隨著目前移植的.NET Micro Framework系統越來越成熟,使用者對資料庫支援的呼聲也越來越高,迫切需要一個資料庫平台了。考慮到移植難度和代碼大小,最初原打算把YFIOs系統中的記憶體YFIODB資料庫修改為檔案版本,這樣移植是最快的,代碼也比較小,但是缺點是比較明顯的,不支援Select等相關的SQL語句,這對熟悉資料庫應用的使用者來說,還需要重新瞭解資料庫的操作方法,還不如用檔案來實現了。
在這個過程中,也瞭解了其它的嵌入式資料庫,如FastDB、Berkeley DB等開源嵌入式資料庫,但其使用者群和知名度遠比不上SQLite,考慮到物聯網中介軟體本身就是一個架構和平台,供使用者二次開發,使用者越熟悉的技術就應該優先選擇。
確定了要移植SQLite資料庫,但是又引出一個問題,是移植SQLite最新的版本?還是以前相對代碼較小的版本?這著實讓我躊躇了良久。
SQLite V2x和V3x比較
項目 |
V2.8.17 |
V3.7.15 |
API介面個數 |
44 |
207 |
源檔案個數 |
44 |
89 |
原始碼位元組數 |
1.32M |
4.0M |
Win32 dll庫大小 |
209K |
591K |
文本編碼支援 |
UTF-8或iso8859 |
UTF-8、UTF-16 |
位元據(Blob) |
不支援 |
支援 |
行編號 |
32位元組 |
64位元組 |
並發性 |
多讀,單寫 |
改良的並發性 |
.NET Micro Framework的核心代碼也不過300K左右,如果支援一個比自己核心還大的多的資料庫,真有點小馬拉大車的感覺,所以在滿足準系統的基礎上(以前我比較擔心V2x版本的國際化應用,比如是否支援中文),代碼大小是我最關注的。
在Windows平台上對SQLite V2.8.17進行測試後,決定移植V2x版本的SQLite(當然後續不排除再移植3.0版本),移植成功後,release版本的 SQLite的大小大概130K左右。
SQLite V2x和V3x .NET Framework開發
考慮到.NET Micro Framework是.NET架構,所以最初研究的是System.Data.SQLite.dll庫,後來發現,一是System.Data.SQLite.dll封裝的太過複雜,二是System.Data.SQLite.dll對.NET Framework的架構非常依賴,並且針對不同平台,很難做到直接拷貝就可以使用(必須要安裝),三是沒有支援SQLite V2x的版本。
所以最後還是決定用Interop的方式直接存取Win32 的 SQLite.dll。網上搜尋了一下,有關於SQLite3的相關樣本,SQLite V2x的就沒有了。所以先研究了一下SQLite3的介面應用,然後根據C++相關的介面定義,反推了一下SQLite V2x的C#介面。
在Windows .NET Framework平台測試SQLite V2x介面(如)沒有問題的情況下,才開始進行.NET Micro Framework 平台下的SQLite V2x的移植。
SQLite V2x .NET Micro Framework移植
理論情況下,只需要對OS.c代碼中的介面進行.NET MF的實現即可(標準的OS.c檔案已經支援Windows,UNIX、Mac OS平台)。但實際移植髮現,遠沒有這麼簡單。
(1)、SQLite代碼都是標準的C語言,但是OS.c中需要調用.NET MF本身的C++代碼操作介面,所以SQLite所有源碼的c副檔名一律修改為cpp介面,採用cpp編譯器進行編譯,但是這樣修改後,會出現很多編譯錯誤,大都是類型轉換的錯誤。
(2)、.NET Micro Framework平台不支援(或不建議)直接採用標準的string.h、ctype.h、math.h和stdio.h等標頭檔定義的函數。.NET Micro Framework代碼中已經有部分字串操作的實現,但是為了實現SQLite的正確編譯,還必須自己補全相關操作函數。時間、檔案等操作函數,也需要轉換為.NET Micro Framework平台下的,或者自己實現。
(3)、記憶體配置相關函數.NET Micro Framework有兩類,一種就是private_開頭的函數,另外就是TinyCLR支援的記憶體操作函數,由於二者使用的堆空間不同(private_開頭的操作Custom_Heap,這個一般都比較小),所以我調試的時候採用private_開頭的函數,實際應用則是TinyCLR記憶體操作函數。把SQLite記憶體相關操作的函數,修改為.NET Micro Framework的。
(4)、由於.NET Micro Framework底層代碼對檔案的操作相對簡單,所以SQLite產生的臨時檔案,我是在上層C#代碼中進行清除的。
(5)、鎖操作,.NET Micro Framework底層是無法對檔案進行鎖定操作的,考慮到.NET Micro Framework本身的特點(單進程,多線程),所以在上層C#代碼中進行了簡單的鎖實現(該鎖讀寫不能同時)。
(6)、SQLite指標應用的非常多,稍有不慎,系統就會出異常,所以在移植過程中,一定要研究透各種介面指標的實際含義(比如定義char ***p這類指標,我以前就很少遇到) ,否則調試過程將是一個噩夢。
SQLite.NET MF應用開發
.NET MF C#介面又進行了封裝,一是介面儘可能和System.Data.SQLite.dll相容,二是做一些必要的記憶體釋放和其它處理(如系統格式化,建立臨時目錄,磁碟排清等等)。
其實對相對簡單的嵌入式應用來說,資料庫無非就是建表,添加和修改資料,刪除,擷取表資料而已,而這些功能都可以通過SQL語句進行實現,所以說功能多,但是介面卻很簡單(如所示)。
測試代碼如下:
public static void Main() { string dbPath = "\\ROOT\\mftest.db"; using (SQLite db = new SQLite(dbPath)) //":memory:")) { //建立表 db.ExecuteNonQuery("CREATE TABLE student(id INTEGER, name VARCHAR(20), sex VARCHAR(2));"); //插入資料 db.ExecuteNonQuery("INSERT INTO student VALUES(1, '小紅', '女');"); db.ExecuteNonQuery("INSERT INTO student VALUES(2, '小李', '男');"); db.ExecuteNonQuery("INSERT INTO student VALUES(3, '小明', '男');"); //讀取表 YFDataTable table = db.ExecuteQuery("SELECT * FROM student"); // WHERE name = '小李';"); string info = ""; foreach (YFColumn col in table.Columns) { info += col.Name + " "; } Debug.Print(info); foreach (YFDataRow row in table.Rows) { Debug.Print(row[0].ToString() + "," + row[1] + "," + row[2]); } } Thread.Sleep(Timeout.Infinite); }
運行調試的情境如所示:
----------------------------------------------------------------------
MF簡介:http://blog.csdn.net/yefanqiu/article/details/5711770
MF資料:http://www.sky-walker.com.cn/News.asp?Id=25
相關硬體: http://www.sky-walker.com.cn/Products.asp?Id=24