本文章是由機器翻譯。

資料點

同步發佈的資料和隔離的存放在 Silverlight

John Papa

可從 MSDN 程式庫 的程式碼下載
瀏覽線上的程式碼

內容

取得已完成
HTTP Web 要求和執行緒處理
新增一個摘要
剖析摘要
跨網域的要求的摘要
基本的隔離儲存區
組織隔離儲存區
wrap-up

Silverlight 是用來建置同步發佈的新聞讀取器應用程式的理想選擇。 它可以讀取 RSS 和 AtomPub 同步發佈的格式會透過 HTTP 的要求,與 Web 服務通訊,並處理跨網域原則。 一旦同步發佈的資料已經收到,可以在類別結構使用 LINQ,所剖析,並呈現給使用者透過以 XAML 為基礎的資料繫結讀取它。

在本專欄中,我將示範如何使用這些獨特的功能來建置同步發佈的新聞讀取器。 我將也示範如何偵錯從 Silverlight 的 Web 服務通訊的問題,以及儲存在本機使用隔離儲存區的資料的如何。 所有範例都是 MSDN 程式碼下載中可用的。

取得已完成

摘要的同步發佈會提供 [RSS] 和 [AtomPub 的格式,透過 Web 服務的存取。 透過,會傳回包含使用 RSS 或 AtomPub 格式摘要的項目的 XML 的 URI 來存取每個摘要。 在範例應用程式,提供這個資料行的會示範讀取到 Silverlight 應用程式中使用 Web 服務要求的同步發佈的資料的技術。 至詳細資料的投擲之前, 可能有用如果我會顯示您已完成的應用程式版本。 接著我將討論邏輯以及應用程式的程式碼的各種其他關於各方面。

[圖 1 顯示讀取這些兩個的同步發佈的摘要 Silverlight 應用程式:

 http://pipes.yahooapis.com/pipes/pipe.run?_id=957­
    d9624940693fb9f9644d7b12fb0e9&_render=rss 
http://pipes.yahooapis.com/pipes/pipe.run?_id=057559bac7aad6640b­
    c17529f3421db0&_render=rss 

fig01.gif

[圖 1 : SilverlightSyndicationApplication

收集摘要的資料,當使用者按一下 [新增] 按鈕時提出 Web 要求。 摘要的 URI 儲存在本機的在用戶端電腦,並摘要服務的標題顯示在上方 data­grid 控制項中。

儲存在 [用戶端電腦] 上的摘要的 URI,可讓 Silverlight 應用程式在應用程式啟動時,取得這些摘要的摘要的資料。 當摘要的資料收集時,項目就會放在物件的清單,並接著剖析使用 LINQ。 結果是再繫結至較低的 data­grid 控制項,已排序的順序。

HTTP Web 要求和執行緒處理

開始使用 Silverlight 應用程式的同步發佈的摘要能夠進行 Web 要求。 同時,Web­client] 和 [HttpWebRequest 類別會讓 HTTP Web 要求 Silverlight 應用程式。 在程序中的第一個步驟決定要用來進行 HTTP Web 要求的技術。 在多數情況下,WebClient 類別,是因為它是簡單的使用需要的所有 (它也會呼叫 HttpWebRequest 私底下)。 HttpWebRequest 允許的要求有更多的自訂。

此程式碼片段會示範如何透過 WebClient 要求:

//feedUri is of type Uri 
WebClient request = new WebClient();
request.DownloadStringCompleted += AddFeedCompleted;
request.DownloadStringAsync(feedUri);

在下列的程式碼從另一方面來說,會示範如何使用 HttpWebRequest 類似呼叫:

//feedUri is of type Uri 
WebRequest request = HttpWebRequest.Create(feedUri);
request.BeginGetResponse(new AsyncCallback(ReadCallback), request);

WebClient 類別會比較容易使用,因為它包裝部分 HttpWeb­request 類別的功能。

Silverlight 的所有 HTTP Web 要求以非同步方式都進行,透過 WebClient 和 HttpWebRequest,以來很重要,要瞭解如何處理資料時,呼叫會傳回。 時 HttpWebRequest 讓在其非同步呼叫完成後不保證操作在 UI 執行緒已完成的事件處理常式。 如果所擷取資料,並且要顯示在 UI 項目,UI 項目呼叫必須進行使用,將控制權傳輸回至 UI 執行緒從背景執行緒的技術。 這可以完成使用發送器或透過 SynchronizationContext 類別。 下列程式碼顯示如何使用 Dispatcher 類別的 BeginInvoke 方法的 UI 執行緒呼叫時:

Deployment.Current.Dispatcher.BeginInvoke(() =>
     {
         MyDataGrid.DataContext = productList;
     });

這個的範例會採用 productList 變數 (的已填入從 Web 服務呼叫傳回的資料),並將它設定為 UI 項目的 DataContext。 在這種情況下一個 DataGrid 會現在繫是結至的產品清單。 發送器就不需要,不過,如果呼叫是透過 WebClient 類別。 在這種情況下,程式碼無法直接將產品清單直接 UI 項目的 DataContext。

若要使用 SynchronizationContext 類別,synchronization­context 類別的執行個體必須建立在 UI 執行緒已知為可用的地方。 建構函式] 和 [Load 事件處理常式都是建立一個 synchronization­context 的執行個體的好地方。 這個程式碼範例會示範在 _syncContext 欄位在類別建構函式中初始化:

public Page() {
_syncContext = SynchronizationContext.Current;
}

此程式碼顯示使用其的 Post 方法進行 LoadProducts 方法呼叫的 SynchronizationContext 執行個體。 可確保 LoadProducts 方法具有 UI 執行緒存取的:

if (_syncContext != null) {
  _syncContext.Post(delegate(object state){ LoadProducts(products); } 
  ,null);
}

除了更容易使用,WebClient 要求永遠回來在 UI 執行緒上。這表示 WebClient 的要求的任何搜尋結果可繫輕鬆地結至 UI 項目而不需要牽涉到發送器 (或,選擇性地,synchronization­context 類別而不是發送器)。用於讀取聯合的資料,WebClient 類別適當並將用於這個資料行的範例應用程式。

新增一個摘要

在範例應用程式,當使用者輸入餵送的地址,並按一下 [新增] 按鈕, [圖 2 ] 所示的程式碼執行。第一次程式碼嘗試從該位址如果可能的話,使用 uri.TryCreate 方法中建立一個 URI。如果它可以建立一個 URI,則 URI 會傳回至本機變數的 feedUri 中。否則,feedUri 保持為 null,並在結束程式碼。

[圖 2 新增一個摘要

private void btnAdd_Click(object sender, RoutedEventArgs e)
{
    Uri feedUri;
    Uri.TryCreate(txtAddress.Text, UriKind.Absolute, out feedUri);
    if (feedUri == null)
        return;

    LoadFeed(feedUri);
}

public void LoadFeed(Uri feedUri)
{
    WebClient request = new WebClient();
    request.DownloadStringCompleted += AddFeedCompleted;
    request.DownloadStringAsync(feedUri);
}

一旦建立有效的 URI,LoadFeed 方法執行,發出 HTTP 要求收集使用 Web­client 類別摘要的資料。 建立,WebClient 執行個體,並的事件處理常式指派給 DownloadStringCompleted 事件]。 DownloadStringAsync 方法執行,並準備好要傳回其資料時, 就需要知道哪些事件處理常式移至。 這就是為何執行非同步事件之前,必須指定事件處理常式 (在本例 AddFeedCompleted)。

一旦完成要求之後,會執行 AddFeedCompleted 事件處理常式 (請參閱 [圖 3 )。 DownloadStringCompleted­EventArgs 參數會包含在結果的屬性] 和 [的錯誤) 」 屬性,兩者的是一定要在每個 Web 要求之後檢查。 e.error 屬性將如果是 null,要求期間發生任何的錯誤。 e.result 屬性會包含在 Web 要求的結果。 範例應用程式,e.result 會包含 XML 表示摘要的資料。

[圖 3 AddFeedCompleted

private void AddFeedCompleted(object sender, 
    DownloadStringCompletedEventArgs e)
{
    if (e.Error != null)
        return;
    string xml = e.Result;
    if (xml.Length == 0)
        return;
    StringReader stringReader = new StringReader(xml);
    XmlReader reader = XmlReader.Create(stringReader);
    SyndicationFeed feed = SyndicationFeed.Load(reader);
    if (_feeds.Where(f => f.Title.Text == feed.Title.Text).ToList().Count > 0)
        return;
    _feeds.Add(feed); // This also saves the feeds to isolated storage
    ReBindAggregatedItems();
    txtAddress.Text = string.Empty;
}

一旦收集資料餵送,它可以讀取到 System.ser­vice­model.SyndicationFeed 類別使用 SyndicationFeed 類別的 Load 方法。 請注意,當擷取摘要的資料,而以唯讀方式) 中使用、 使用 LINQ to XML,以擷取摘要,以及載入自訂物件中可能比 SyndicationFeed 一個更好的選項。 SyndicationFeed 有更多的功能中,但是如果它們不會使用它不一定需要額外的大小加入至 [XAP — SyndicationFeed 加入關於 150KB,XAP LINQ to XML 加入關於 40KB 時。 SyndicationFeed 的其他強大功能與您也可以有些成本的大小。

SyndicationFeed 會是特殊類別知道如何表示摘要 (RSS 和 AtomPub) 的資料,為物件。 它具有屬性,描述本身,例如標題和描述,摘要,以及包含 IEnumerable <syndicationitem> 的項目屬性。 每個 SyndicationItem 類別執行個體會表示一個摘要的項目,摘要。 舉例來說,摘要由 SyndicationFeed,類別的執行個體,而其項目的集合包含個別的內容,從摘要。

一旦 SyndicationFeed 類別使用摘要和其項目載入,在程式碼,如 [圖 3 ] 所示會檢查是否相同的摘要已已經被收集。 如果是,程式碼就會立即結束。 否則,摘要會加入稱為 _feeds ObservableCollection 本機 <Syndication­Feed>。 透過 ReBindAggregatedItems) 方法摘要來自所有載入的摘要項目會再篩選、 排序,並繫結較低的 DataGrid 至。 因為 WebClient 類別會發出 HTTP Web 要求,AddFeedCompleted 事件處理常式都可存取至 UI 執行緒。 這就是為何 ReBind­ag­gregatedItems 方法內的程式碼可以將在資料繫結 DataGrid 的 UI 項目沒有發送器的 [說明]。

剖析摘要

執行 ReBindAggregatedItems 方法時, 則會將摘要的資料儲存在 SyndicatedFeed 的執行個體的集合和 SyndicatedItem 的執行個體其個別集合。 LINQ 是最理想的查詢摘要的資料,因為它是目前的物件結構的。 載入 SyndicatedFeed 物件不需要資料。 而它可能有已存放在原生 XML 格式 (以 RSS 或 AtomPub),它可能有已剖析使用 XmlReader 或 LINQ to XML。 但是,SyndicatedFeed 類別方便管理,並 LINQ 還是可用來查詢資料。

顯示數個摘要的摘要的項目時,需要的摘要的項目的所有 mashed 一起。 [圖 4 ] 所示,LINQ 查詢會示範如何摘要 (SyndicationFeed 執行個體) 的所有擷取的所有摘要項目 (Syndication­Item 執行個體),及排序它們其發行日期。

[圖 4] 正在查詢使用 LINQ 的摘要

private void ReBindAggregatedItems()
{
    //Read the feed items and bind them to the lower list
    var query = from f in _feeds
                from i in f.Items
                orderby i.PublishDate descending
                select new SyndicationItemExtra
                        { FeedTitle = f.Title.Text, Item = i };

    var items = query.ToList();
    feedItemsGridLayout.DataContext = items;
}

請注意,在 [圖 4] 中查詢會傳回 SyndicationItemExtra 類別的清單。在的 SyndicationItemExtra 類別是一個自訂的類別具有 FeedTitle 字串屬性的型別和型別 SyndicationItem 的項目屬性。應用程式會顯示在的 DataGrid 中的摘要的項目,也大部分的資料可以被找到 SyndicationItem 類別中]。

不過,因為應用程式一起 mashes 來自數個摘要項目,顯示摘要每個摘要項目的標題可清除的摘要每個項目是從。摘要的標題不 SyndicationItem 類別,從存取的因此應用程式使用自訂類別,呼叫將會儲存在 SyndicationItem] 和 [摘要的標題的 SyndicationItemExtra。

摘要的項目然後繫結至格線面板 feedItemsGridLayout Silverlight 應用程式中。在 Grid 面板包含的 DataGrid,以及其他 UI 項目 (例如在 TextBlock 中顯示的項目數) 所牽涉的資料繫結作業,以顯示摘要項目的資訊。

跨網域的要求的摘要

收集餵送的要求是 HTTP Web 要求,通常會提出要求至另一個 Web 網域中。任何從 Silverlight 與不同的網域通訊,裝載的 Silverlight 應用程式的 Web 要求必須符合遠端網域的跨網域原則。[圖 5 ] 中的圖表將示範這種情況。

fig05.gif

[圖 5 跨網域呼叫的資料餵送

如需有關跨網域原則的詳細資訊,請參閱我9 月 2008 資料點的資料行. 該資料行中的我會討論檔案格式] 和 [原則的運作方式。

跨定義域的 HTTP Web 要求進行時, Silverlight preempts 要求第一個要求跨網域的原則檔從遠端 Web 伺服器。Silverlight 首先會尋找,clientaccesspolicy.xml 檔案 (Silverlight 跨網域的原則檔),並如果未找到,它再尋找 crossdomain.xml 檔 (Flash 的跨網域原則檔)。如果找到任何一個檔案,要求失敗,並會擲回錯誤。可以在 DownloadStringCompleted 事件處理常式中攔截這個錯誤,並呈現給的使用者中,如果需要的話。

例如,如果在 URI http://johnpapa。net/feed/default.aspx 進入範例應用程式,Silverlight 將會先尋找其中一個 johnpapa 上跨網域的原則檔案。net Web 伺服器的根目錄。如果檔案都是找不到,則錯誤傳回至應用程式,點的應用程式可以通知使用者如果需要的話。[圖 6 ] 顯示 [FireBug 外掛程式,會追蹤從瀏覽器的所有要求的。它會顯示尋找跨網域的原則檔案無法尋找它們,並在傳回而不進行要求的 RSS 餵送,實際上,瀏覽器。

fig06.gif

[圖 6 偵錯的跨網域摘要呼叫

fireBug 是一個好工具監看的 Firefox,HTTP 要求,而 Web 開發 Helper 會是很棒的工具,使用 Internet Explorer 時]。另一個選項 Fiddler2,也就是獨立的應用程式,可以查看電腦上的所有流量。

這個問題的一個解決方案是要求 Web 系統管理員,摘要的 Web 伺服器的根目錄中存放 clientaccesspolicy.xml 的檔案。這可能無法實際,因為您很可能並不會控制遠端 Web 伺服器,或您知道使用者不。另一個選項是請參閱摘要是否使用的中繼的服務,例如 Yahoo 的管道。例如,主要餵送在 johnpapa。可以透過使用 URI http://pipes.yahooapis.com/pipes/pipe.run?\_id=057559bac7aad6640bc17529f­3421db0&\_render=rss 的 Yahoo 管道中擷取網路。因為有跨網域的原則檔位於可開啟的 Access http://pipes.yahooapis.com/clientaccesspolicy.xml 這會是一個好的替代方法。

第三個選項是使用服務,例如 Popfly 或 FeedBurner 彙總有效地將它們轉送透過服務也有一個開啟的跨網域原則的摘要。最後,在第四個選項是撰寫您自己自訂 Web 服務會收集摘要,然後將它們轉送到 Silverlight 應用程式。使用服務,例如 Popfly 或 Yahoo 的管道,提供最簡單的解決方案。

基本的隔離儲存區

範例應用程式,可讓使用者新增數個摘要,並將每一個這些摘要中檢視的所有項目。如果使用者輸入 10 個摘要,並決定他必須關閉應用程式,並有回更新的版本來讀取它們,他可能會預期應用程式會記住摘要。否則,他就必須每次開啟應用程式時,輸入每個摘要的 URI。因為這些摘要是特定的使用者,它們能夠儲存使用某些語彙基元來識別輸入他們的使用者在伺服器或使用者的電腦上。

Silverlight 可將資料儲存到受保護區域的使用者的電腦,使用 System.IO.IsolatedStorage 命名空間中的類別。就像 steroids 上的 Cookie 的 Silverlight 隔離儲存區: 它可讓您儲存簡單的純量值] 或 [甚至儲存在用戶端電腦上的序列化的複雜物件圖形。將儲存至隔離儲存區,最簡單方式為: 建立 ApplicationSettings 項目中,並在把您的資料,如下所示:

private void SaveFeedsToStorage_UsingSettings()
{
    string data = GetFeedsFromStorage_UsingSettings() + FEED_DELIMITER + 
        txtAddress.Text;
    if (IsolatedStorageSettings.ApplicationSettings.Contains(FEED_DATA))
        IsolatedStorageSettings.ApplicationSettings[FEED_DATA] = data;
    else
        IsolatedStorageSettings.ApplicationSettings.Add(FEED_DATA, data);
}

<syndicationfeed>這可以呼叫每次在 SyndicationFeed 加入或移除從呼叫 _feeds Ob­serv­ableCollection < SyndicationFeed > 欄位執行個體。 因為在 ObservableCollection 會公開 CollectionChanged 事件,處理常式可以被指派給執行在的儲存,如下所示的事件中:

_feeds.CollectionChanged += ((sender, e) => { 
                               SaveFeedsToStorage_UsingSettings(); });

執行 SaveFeedsToStorage_UsingSettings 方法時, 它會先呼叫 GetFeedsFromStorage_UsingSettings 方法,會抓取所有摘要從隔離儲存區的位址,並將它們置於特殊字元分隔為單一字串。

應用程式第一次啟動時的 LoadFeedsFromStorage_UsingSettings 方法會擷取摘要,從隔離儲存區:

private void LoadFeedsFromStorage_UsingSettings()
{
    string data = LoadFeedsFromStorage_UsingSettings();
    string[] feedList = data.Split(new string[1] { FEED_DELIMITER }, 
      StringSplitOptions.RemoveEmptyEntries);
    foreach (var address in feedList)
        LoadFeed(new Uri(address));
}

程式碼首先會從隔離儲存區讀取 URI 的位址的每個摘要的清單。 然後它會逐一查看位址,並載入摘要資料一次一個每個人使用 LoadFeed 方法。

組織隔離儲存區

這項功能,可讓記住使用者的餵送位址,並當使用者執行應用程式時,請載入這些應用程式。 封裝 URI 的位址到分隔字串很簡單,但既不是簡潔,也不是 expansive 」 中。 例如,如果要儲存不只是單一純量值這會取得複雜使用這項技術。

其他的方式,將資料儲存在隔離儲存區,為使用 isolated­StorageFile 和您儲存複雜的資料結構包括哪些讓序列化的每個使用者的物件的 IsolatedStorageFileStream 類別。 資料可以甚至區隔成不同的檔案和隔離儲存區中的資料夾。 這是適合組織將儲存在隔離儲存區 (Isolated Storage) 的資料。 例如,資料夾無法會建立所有的資料的靜態清單],並無法建立每個清單不同的檔案。 所以隔離儲存區 (Isolated Storage) 中的資料夾,內,檔案可能存在的另一個用於性別,名稱首碼和另一個的 U.S. 說明。

在隔離儲存區包含 URI 的位址的清單中,範例應用程式無法建立檔案。 在資料先必須序列化然後再傳送給該檔案在隔離儲存區中 (如 [圖 7 ] 所示)。 先,IsolatedStorageFile) 類別,針對目前的使用者的執行個體是使用 GetUserStoreForApplication 方法建立的。 就讓應用程式可以寫入 URI 位址,會建立檔案資料流。 資料,然後會序列化,並寫入 isolated­StorageFileStream 的執行個體。 在的範例,此應用程式會序列化資料的字串,但可以任何可序列化的物件寫入隔離的儲存區]。

[圖 7 儲存序列化資料的隔離儲存區檔案

private void SaveFeedsToStorage_UsingFile() {
    using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
        List<string> data = GetFeedsFromStorage_UsingFile();
        if (data == null)
            if (txtAddress.Text.Length == 0)
                return;
            else
                data = new List<string>();
         using (var isoStoreFileStream =
                new IsolatedStorageFileStream(FEED_FILENAME,
                   FileMode.Create, isoStore)) {
            data.Add(txtAddress.Text);
            byte[] bytes = Serialize(data);
            isoStoreFileStream.Write(bytes, 0, bytes.Length);
        }
    }
}

從隔離儲存區中讀取檔案時的序列化的資料稍微更涉及比先前的範例。 [圖 8 ] 顯示的第一個您必須取得的使用者的 IsolatedStorageFile 類別的執行個體,然後檢查檔案是否存在之前,您可以讀取它。 如果檔案存在,被開啟檔案,透過 IsolatedStorageFileStream 的型別的資料流可讀取資料的讀取存取的進行。 資料是從資料流讀取、 將放在一起,,然後還原序列化所以它可以使用要載入的同步發佈的摘要中。

[圖 8 讀取序列化資料的隔離儲存區檔案

private List<string> GetFeedsFromStorage_UsingFile() {
    byte[] feedBytes;
    var ms = new MemoryStream();
    using (var isoStore = 
      solatedStorageFile.GetUserStoreForApplication())
    {
        if (!isoStore.FileExists(FEED_FILENAME)) return null;
        using (var stream = isoStore.OpenFile(FEED_FILENAME, 
          FileMode.Open, FileAccess.Read))  {
            while (true) {
                byte[] tempBytes = new byte[1024];
                int read = stream.Read(tempBytes, 0, tempBytes.Length);
                if (read <= 0) {
                    //feedBytes = ms.ToArray();
                    break;
                }
                ms.Write(tempBytes, 0, read);
            }
        }
        feedBytes = ms.ToArray();
        List<string> feedList = Deserialize(typeof(List<string>), 
            feedBytes) as List<string>;
        return feedList;
    }
}

private void LoadFeedsFromStorage_UsingFile() {
    var feedList = GetFeedsFromStorage_UsingFile();
    foreach (var address in feedList) {
        Uri feedUri;
        Uri.TryCreate(address, UriKind.Absolute, out feedUri);
        if (feedUri != null)
            LoadFeed(feedUri);
    }
}

簡單的資料結構的使用序列化物件,而且隔離儲存區中的檔案可能無法需要]。不過,隔離儲存區用於本機存放區的數種時, 它可協助組織資料,並提供對讀取] 和 [寫入的輕鬆存取。隔離儲存區應該用來好好儲存應該在本機快取的資料。

同時也請注意使用者可以最後清除儲存在任何時間因為它們有完全控制其設定。這表示儲存在隔離儲存區中的資料不應視為保證的持續性儲存體。另一個例子儲存一份 U.S.隔離儲存區 (Isolated Storage) 中的說明讓 Web 要求 (與資料庫的點擊) 不需要進行每次需要美國的清單中會填入一個下拉式方塊說明。

wrap-up

在的範例應用程式會示範如何是容易 Silverlight 應用程式中載入 RSS 和 AtomPub 的摘要。Silverlight 能夠進行 Web 要求、 接受其結果、 處理跨網域原則的呼叫,摘要的資料載入 SyndicationFeed 類別、 使用 LINQ 查詢、 將它們繫結至 UI 的項目和隔離儲存區 (Isolated Storage) 中的儲存摘要的資料。

上個月和這個月,Hanu Kommalapati 會討論建置 Line-of-Business 應用程式,您可以閱讀他的文章的 Silverlight 」Silverlight: 建置使用 Silverlight,第 1 部的 Line-Of-Business 企業應用程式「 和 」Silverlight: 建置使用 Silverlight,第 2 部的 Line-Of-Business 企業應用程式."

您提出問題或意見,請將 John 寄mmdata@Microsoft.com.

John Papa (johnpapa。net) 是資深的顧問,與ASPSOFT並棒球風扇者花費夏天住宿固定 Kadi 與他的家人。John,使用 C# MVP 和 INETA 演說,已撰寫數個書籍,,現在正在他的最新,在標題為 Data-Driven Services with Silverlight 2。將說出而他通常呈在例如 DevConnections 和 VSLive 會議。