Silverlight 2.0
資料庫應用程式開發
(1)
文
/
黃忠成
從
Silverlight 1.0
到
Silverlight 2.0
我想不用我贅言,相較於
Silverlight 1.0
的陽春,
Silverlight 2.0
提供了更完整的資料庫支援,其最主要的部份在於提供了
WCF/HTTP
的網路機制及客戶端的控件
Binding
技術,有了這兩個機制,我們可以透過
WCF/HTTP
網路機制連回
Server
端取得資料,然後以
Binding
技術將控件與資料結合在一起,設計出類似
Windows Form/WPF UI
介面的強大網頁資料庫應用程式。
Silverlight 2.0
與
N-Tier
對於部份的
ASP.NET
設計師而言,對
Silverlight 2.0
的資料庫應用程式的開發模式會有些陌生,在
ASP.NET
設計模式中,我們可以直接使用
ADO.NET
來取得資料,接著利用
GridView
等資料控件將資料顯現於網頁上,此時的系統架構如圖
SL001
。
圖
SL001
但在
Silverlight 2.0
中,並未提供
ADO.NET
這個機制,從網頁應用程式角度上看,
Silverlight 2.0
是一種
Client Application
架構,運行於
IE
等瀏覽器平台之上,因此若要提供
ADO.NET
機制,那麼後端的資料庫必定得曝露於網際網路之上,而你肯定無法接受將
SQL Server
曝露在網路上供人存取,因為這會帶來安全性、授權費等問題。所以,當使用
Silverlight 2.0
時,我們必須找一個相對於
ADO.NET
的機制來做為取得資料的中介層,這種設計模式就稱為
N-Tier
,見圖
SL002
。
圖
SL002
如圖
SL002
所示,
Silverlight
應用程式是內嵌於
HTML
中下載,她可以透過
WCF/HttpWebRequest
網路機制連回來源的
Web Site
取得資料,問題在於後端的
Web Site
如何接收來自
Silverlight
的網路要求?在
Silverlight 2.0
中,你有兩種選擇,一是在
Web Site
端建立一個簡單的網頁,收取來自
Silverlight
的網路要求,接著輸出
XML
或是
JSON
格式的資料。二是透過
.NET
的
WCF
機制,於
Web Site
端建立一個
WCF Service
,於
Silverlight
中透過此
WCF Service
來取得資料。兩種機制各有其優缺點,方法一可以適用於所有網頁平台,例如
ASP
、
PHP
、
JSP
皆可,而方法二則是僅限於支援
Web Service
的網頁平台。
在開始設計實際應用程式之時,你必須先了解所謂
N-Tier
架構的定義,所謂的
N-Tier
指的是將整個程式架構切分為三個層面,第一層是展現層,也就是你的
Silverlight
應用程式,用來顯示資料及接受使用者操作,第二層指的服務層,用來提供資料及回應來自展示層的要求,第三層則是資料層,用來由資料庫取得資料或是更新資料。
N-Tier
早期的發展是為了分擔資料庫系統的繁重工作,以
[
偶而連線
]
、
[
負載平衡
]
等技巧,減輕本來必須全部交與資料庫處理的工作。舉個例來說,一系統擁有
10000
個使用者,當使用傳統的
Client/Server
架構時,這
10000
個使用者會握有
10000
個資料連線,當這些使用者發出要求資料的
SELECT
需求時,資料庫系統就得負擔產生
10000
個資料集的工作,但這
10000
個需求中,很有可能有一半以上是要求同一份資料。當使用
N-Tier
架構時,我們就可以在服務層中快取已取過的資料,當有同樣要求來臨時,直接以回傳快取資料的方式取代由資料庫系統取得的動作,這樣自然就減輕了資料庫的負擔,提高系統所能承載的用戶數量。
在早期網際網路頻寬不足的環境下,
N-Tier
架構原始設計中每個動作都要透過連結服務層的模式顯得有些不切實際,因為使用者可能處於一個低頻寬的環境、甚至是無網路可用的窘境,在這種模式下,存取服務層是種奢侈。因此,
N-Tier
發展出一種名為
[
偶爾連線
]
的模式,展示層在有網路的情況下,將資料由服務層取回後快取於客戶端,此時使用者可以自由的操作展示層來新增、修改、刪除及查詢資料,這些動作全以客戶端的快取為主,待有網路時,再將異動資料整批傳回服務層更新回資料庫。
[
偶爾連線
]
的架構很完美,也很符合當年的需求,但隨著網路的普遍化及高覆蓋率,設計
[
偶而連線
]
架構所需付出的代價就顯得有些累贅了,
[
偶爾連線
]
的架構建立在完善的客戶端快取機制及客戶端查詢機制下,而這些機制並不容易建構。所以,現在的
N-Tier
轉變為以
[
連線模式
]
為主。
Silverlight 2.0
的架構趨近於
[
連線模式
]
,這意味著
Silverlight 2.0
的應用程式將運行在一個可存取網站的環境下,當這個網站存在於本機時,程式不需要網路便可執行,當網站不存在於本機時,則需要網路方能運行。
註:多數瀏覽器都有一種以離線模式執行網頁的設定,將離線模式打開,你依舊能在離線模式下運行
Silverlight
程式,在適當的設計下,其實也能做出
[
偶爾連線
]
模式。
|
使用
HttpWebRequest
在大略了解
Silverlight 2.0
與
N-Tier
架構後,我們便可以開始撰寫
Silverlight 2.0
的資料庫應用程式,請建立一個
Silverlight 2.0
專案,接著於
Web
結尾之
Project
添加一資料庫。
圖
SL003
圖
SL004
然後添加一資料表至資料庫中。
圖
SL005
建立結構。
圖
SL006
添加資料列。
圖
SL007
圖
SL008
完成資料庫準備工作後,現在便可開始實作服務層,在這小節中,我們以傳統網頁模式來建立服務層,請於
Web
結尾之專案中添加一
Generic Handler(
你也可以使用
.aspx)
。
圖
SL009
DBProvider.ashx.cs
|
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Data;
using
System.Data.SqlClient;
using
System.Configuration;
using
System.Xml.Linq;
namespace
DBDemo1.Web
{
///<summary>
/// Summary description for $codebehindclassname$
///</summary>
public class DBProvider : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/xml";
using (SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\MyDatabase.mdf;Integrated Security=True;User Instance=True"))
{
XDocument doc = new XDocument(new XElement("Root"));
using (SqlCommand cmd = new SqlCommand("SELECT * FROM CUSTOMERS", conn))
{
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader(
CommandBehavior.CloseConnection))
{
while (reader.Read())
{
XElement elem = new XElement("Customer");
elem.Add(new XAttribute("CUSTOMER_ID",
reader.GetString(reader.GetOrdinal("CUSTOMER_ID"))));
elem.Add(new XAttribute("CUSTOMER_NAME",
reader.GetString(reader.GetOrdinal("CUSTOMER_NAME"))));
doc.Root.Add(elem);
}
}
context.Response.Write(doc.ToString());
context.Response.Flush();
context.Response.End();
}
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
|
透過
IE
來測試此服務層是否運作正常。
圖
SL010
由於本例將使用
DataGrid
控件,因此你必須透過
Add Reference(
加入參考
)
來添加
DataGrid
所需要的
Assembly
至
Silverlight
專案
(
沒有
Web
結尾的那一個
)
。
圖
SL011
圖
SL012
另外,由於服務層使用
XML
,我們可以利用
Silverlight 2.0
所提供的
LINQ To XML
來簡化解譯
XML
的工作,
LINQ To XML
需要添加
System.XML.Linq.dll
為參考,請透過
Add Reference
來添加
System.XML.Linq.dll
至
Silverlight
專案中
(
沒有
Web
結尾的那一個
)
。
圖
SL013
接著在
Page.xaml
中鍵入以下的
XAML
。
Page.xaml
|
<
UserControl
x
:
Class
="DBDemo1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
Width="400" Height="300" Loaded="UserControl_Loaded">
<
Grid
x
:
Name
="LayoutRoot"
Background
="White"
Height
="300"
Width
="400">
<
data
:
DataGrid
x
:
Name
="grid"
AutoGenerateColumns
="True">
</
data
:
DataGrid
>
</
Grid
>
</
UserControl
>
|
最後在
Page.xaml.cs
中鍵入以下的程式碼。
Page.xaml.cs
|
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Net;
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Documents;
using
System.Windows.Input;
using
System.Windows.Media;
using
System.Windows.Media.Animation;
using
System.Windows.Shapes;
using
System.IO;
using
System.Xml;
using
System.Xml.Linq;
namespace
DBDemo1
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
new Uri("http://localhost:40419/DBProvider.ashx", UriKind.Absolute));
request.Method = "POST";
request.BeginGetRequestStream(new AsyncCallback(ReadCallback),request);
}
private void ReadCallback(IAsyncResult state)
{
HttpWebRequest request = (HttpWebRequest)state.AsyncState;
Stream postStream = request.EndGetRequestStream(state);
//byte[] buff = System.Text.Encoding.Unicode.GetBytes("TEST");
//postStream.Write(buff, 0, buff.Length);
postStream.Close();// the request stream must closed before
request.BeginGetResponse(new AsyncCallback(GetResponse), request);
}
private void GetResponse(IAsyncResult state)
{
HttpWebRequest request = (HttpWebRequest)state.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(state);
using (Stream stream = response.GetResponseStream())
{
XDocument doc = XDocument.Load(stream);
Dispatcher.BeginInvoke(
new System.Threading.ParameterizedThreadStart(UpdateUI),doc);
}
}
private void UpdateUI(object state)
{
XDocument doc = (XDocument)state;
grid.ItemsSource = (from s1 in doc.Elements("Root").Descendants("Customer")
select new Customer()
{
ID = s1.Attribute("CUSTOMER_ID").Value,
Name = s1.Attribute("CUSTOMER_NAME").Value
}).ToList();
}
}
public class Customer
{
public string ID { get; set; }
public string Name { get; set; }
}
}
|
圖
SL014
是此例執行結果。
圖
SL014
透過
HttpWebRequest
的模式,我們可以用傳統網頁來扮演服務層,這可應用於多數網頁平台如
PHP
、
ASP
、
JSP
之上。
使用
WCF
使用
HTTPWebRequest
的方式雖然簡單,但缺點是我們得自訂其間資料傳送的格式,最大化相容性的結果也限縮了結構性,所幸在現今網路世界中早已定義了多數網頁平台都支援的資料格式,那就是
SOAP/Web Service
。
Silverlight 2.0
除了允許我們撰寫
Web Service
的客戶端外,同時也支援了
.NET Framework 3.0
中所新增的
WCF Service
,本例就以
WCF Service
做為服務層,請添加一個
Web Service
至
Web
結尾的專案中。
圖
SL015
然後添加一個
LINQ To SQL Classes
至
Web
結尾的專案中,做為取代
ADO.NET
的資料存取機制。
圖
SL016
添加資料表至
.DBML
中。
圖
SL017
刪除自動產生的
IDBService.cs
,然後於
DBService.svc.cs
中鍵入以下程式碼。
DBService.svc.cs
|
using
System;
using
System.Collections.Generic;
|