本文章是由機器翻譯。

資料點

使用 ADO.NET 資料服務中使用 Silverlight 2

John Papa

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

內容

跨網域的通訊
智慧型的關於機密資料
快速快速入門 EDM
參考從 Silverlight 的 ADO.NET 資料服務
使用 LINQ 擷取資料
延遲載入
儲存資料
預先擷取資料
若要將無法繼續

ADO.NET 資料服務容易極為公開資料,並將允許更新透過 HTTP 使用其 RESTful (Representational 狀態傳輸) 的能力。您的 Silverlight 應用程式可以利用 ADO.NET 資料服務,以傳送和接收使用唯一的 URI 對應至實體的資料。您也可以在 Silverlight 用戶端使用 LINQ 查詢,來使用 ADO.NET 資料服務 Silverlight 用戶端程式庫,在伺服器上的實體與互動。

ADO.NET 資料服務和 Silverlight 讓強大組合但要請他們合作無間您會需要幾方面,可能無法立即明顯的了解。所以,這裡我將位址可確保在使用 ADO.NET 資料建置應用程式服務時更成功的經驗和 Silverlight 的某些步驟。

跨網域的通訊

ADO.NET 資料服務目前不支援跨網域的通訊。標準 REST 和 SOAP 服務,但不是 ADO.NET 資料服務,支援的跨網域的通訊。(成便箋: 資料服務小組探索空間,並會張貼至其進度Astoria 小組部落格他們請),。 這表示如果這些服務裝載在比網域裝載 Silverlight 的用戶端應用程式的不同網域上,透過 ADO.NET 資料服務所公開的服務無法與 Silverlight 2 用戶端。如需跨網域原則的詳細資訊,請參閱我9 月 2008 」 資料點 」 的資料行. 該的資料行中的我會討論檔案格式]和 [原則的運作方式。

智慧型的關於機密資料

時使用 HTTP 通訊透過 URI 中傳送的所有值都將都會清楚地出現,透過各種不同的網路竊聽工具。其中一個方法戰鬥這是使用 SSL 來加密所有的 HTTP 通訊。而且,傳送 URI 沒有機密] 資料的例如證字號或任何其他私密的資料。它是很好不會],做為識別項上的機密資料。選擇任何 RESTful 通訊前, 務必使用例如 GUID)、 數字或的 IDENTITY 值為無意義的識別項。例如,下列的範例 URI 無法擷取使用 11 的員工 ID 的員工資料:

http://[YourDomainHere]/MyService.svc/employee(11)

11 數目是透明的在 URI 就是為什麼很重要使用機密的資訊,原因的。幸運的是,在這個範例數字 11 表示為 fabricated 的識別項因此不公開的任何個人資料。

快速快速入門 EDM

可能是最簡單設定取得的方法與執行使用 ADO.NET 資料服務是成使用 ADO.NET Entity Framework 的邏輯實體模型公開關聯式資料庫的資料。Entity Framework 的實體資料模型 (EDM) 是完全瞭解如何允許讀取和更新存取其項目的大小寫。EDM 的這項內建的功能可讓它與 ADO.NET 非常少的安裝程式的資料服務正常運作。

公開 EDM Entity Framework 以建立第一個步驟是建立 ADO.NET 資料服務的服務,從 [Visual Studio 範本] 對話方塊。這會建立輕鬆地可以公開從 Entity Framework EDM 中修改的樣板。類別建構函式會繼承自 [DataService <t> 基底類別。T 會表示資料來源類別,這在這種情況下是實體架構資料來源類別名為實體。資料來源類別也稱為物件的內容類別的 Entity Framework,會是允許 EDM 擷取和儲存資料的存取。因為資料來源類別會自動建立與實體 Framework,建立公開 (Expose) EDM 的 ADO.NET 資料服務,是相當簡單。在 [圖 1 ,程式碼會顯示在服務類別 NWDataService 繼承自 [DataService <entities>。

[圖 1) 建立,DataService

public class NWDataService : DataService<Entities>
//public class NWDataService: DataService< /* TODO: put your data source 
//class name here */ >
 {
    // This method is called only once to initialize service-wide 
    //policies.
    public static void InitializeService(IDataServiceConfiguration 
        config)
    {
        //set rules to indicate which entity sets and service operations 
        //are visible, updatable, etc.
        config.SetEntitySetAccessRule("ProductSet", EntitySetRights.All);
        config.SetEntitySetAccessRule("CategorySet", 
            EntitySetRights.AllRead);
        config.SetEntitySetAccessRule("SupplierSet", 
            EntitySetRights.AllRead);
        config.SetEntitySetAccessRule("OrderSet", 
            EntitySetRights.AllRead);
        config.SetEntitySetAccessRule("OrderDetailSet", 
            EntitySetRights.All);
        config.SetEntitySetAccessRule("CustomerSet", 
            EntitySetRights.AllRead);
        /// The rest of the entity sets are not accessible.
        /// Therefore, no proxy classes are created for them either.
    }
}

下一個步驟是在若要允許或拒絕讀取寫入每個實體的存取權限中並設定 EDM 如 [圖 1 ] 所示)。 這可以使用的所有實體集上完成,* 或上的個別的實體設定層級,藉由指定每個實體集的名稱。 在 SetEntitySetAccessRule 方法可接受的名稱之規則將套用至實體集和一或多個 EntitySetRights 列舉值。 請注意 [圖 2 ] 中每個實體設定 ProductSet、 順序集、 OrderDetailSet、 CustomerSet、 SupplierSet,並 CategorySet 允許所有的存取。 這表示這些六個實體設定允許讀取、 插入、 更新,和刪除。 這些會是服務的整個存取設定,並將套用至進入系統的所有要求。 無法存取未列出例如 EmployeeSet 或 RegionSet,任何實體集。

[圖 2 System.Data.Services EntitySetRights 列舉
列舉 描述
所有 允許指定之實體上所有讀取和寫入
AllRead 允許所有讀取
AllWrite 所有寫入所允許的作業
允許指定之實體沒有存取權
ReadMultiple 允許讀取多個資料列
ReadSingle 允許讀取單一資料列
WriteAppend 允許建立新的資料
WriteDelete 允許刪除資料
WriteMerge 允許合併為基礎的更新程式
WriteReplace 取代為允許

EntitySetRights 列舉值會表示允許實體集的存取類型。 [圖 2 ] 顯示所有有效的列舉值及其說明。 使用權限可以是指定在全域規模上所有的實體集,太。 例如,下列的程式碼行就會允許對所有的實體的所有讀取存取設定的但將不允許的寫入存取權:

config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);

權限都可以結合為允許一個以上的實體集,以及的權限。 下列程式碼會進行設定以讀取和更新只能存取 ProductSet 實體:

config.SetEntitySetAccessRule("ProductSet", 
EntitySetRights.AllRead | 
EntitySetRights.WriteMerge | 
EntitySetRights.WriteReplace);

依照下列步驟執行,會公開 (Expose) 六個項目集合 (如 [圖 1 ] 所示),並讓它們可透過的 ADO.NET 資料服務。 其他的實體設定和使用權限可以太自訂。

實體模型,建置的其他物件關聯對應 (ORM)] s 例如 LINQ to SQL 和 NHibernate,也可用於與 ADO.NET 資料服務。 在內容物件必須要查詢的每個實體集,實作 IQueryable。 建立、 更新程式,及刪除作業,內容物件必須實作,可 IUpdatable 介面。 ADO.NET 資料服務會有內建的知識,ADO.NET Entity Framework,讓它支援查詢和更新透過 ADO.NET 資料服務。 很可能在其他 ORMs 的未來版本也有支援 ADO.NET 資料服務或至少有協助程式類別,以協助您加快它。 但現在,如果您要使用 ADO.NET 資料服務,以外的 Entity Framework 的 ORM 您將需要實作上述的介面。 事實上,ADO.NET 資料服務是不特定的關聯式資料。 透過這個段落所述的技術,可以設定任何資料來源。

fig03.gif

[圖 3] 產生的類別,從 [服務參考

參考從 Silverlight 的 ADO.NET 資料服務

一旦建立並建置 ADO.NET 資料服務,Silverlight 應用程式就可以參考服務,並與其互動。 可以滑鼠右鍵按一下在方案總管中開啟 [新增服務參考] 對話視窗的 [服務參考] 節點,將的 ADO.NET 資料服務參考從 Silverlight 應用程式。 您可能可以在位址方塊中輸入服務的 URI,或可以按一下 [探索,] 按鈕,然後按一下 [執行] 按鈕來尋找服務。 一旦擷取中繼資料服務的服務和它會公開 (Expose) 在項目集合會出現在清單中。 最後,名稱服務) 參考,然後按一下 [確定的按鈕。

這會建立在 Silverlight 用戶端,可讓您與 ADO.NET 資料服務互動的 Proxy 類別。 如果您按一下 [顯示 [方案總管] 視窗中的所有檔案] 按鈕,完全展開 NWServiceReference 節點時,您會看到 reference.cs 檔案。 這個檔案包含產生的 Proxy 類別,可讓您與 ADO.NET 資料服務互動 」 和 「 實體透過服務公開 (Expose) 的產生的類別。 查看 [類別檢視] 視窗,如 [圖 3 ] 所示,也可以看到的類別清單。

請注意在 Web 專案中 Entity Framework 的模型中實體的所有未列在類別檢視]。 只可透過資料服務存取的實體集,會都有當從 Silverlight 的用戶端服務的參考加入至 ADO.NET 資料服務時,為它們所產生的 Proxy 類別。 在類別檢視中顯示的六個項目集合公開在 ADO.NET 資料服務中使用 SetEntitySetAccessRule 方法,因此這些 Silverlight 用戶端可用的唯一實體集。 類別檢視] 視窗的實體類別,中的第七個類別會是一個: Proxy 類別代表整個資料服務,有助於 NWDataService.svc 呼叫。

使用 LINQ 擷取資料

下一個步驟是在 Silverlight 中的 System.Data.Services.client 組件專案 (請參閱 [圖 4 ) 的參考。 這個組件可讓您輕鬆與使用 LINQ,ADO.NET 資料服務互動。 例如此之外,下列的程式碼也會建立一個選取的所有產品使用 ADO.NET 資料服務的 LINQ 查詢:

  _products.Clear();
  DataServiceQuery<Product> dq = (from p in _ctx.ProductSet select p)
  as
  DataServiceQuery<Product>;
  dq.BeginExecute(new AsyncCallback(FindProduct_Completed), dq);

fig04.gif

[圖 4] 參考,ADO.NET 資料服務 ClientLibrary 從 Silverlight

在這段程式碼,LINQ 查詢是轉譯成 ADO.NET 資料服務可讀取的 URI 中。 查詢會轉換為一個 DataServiceQuery <t>,它在是 System.Data.Services.Client 命名空間的一部分。 查詢執行以非同步方式,所以必須指定有效的回呼方法,才能接收從查詢的結果。 時在上述程式碼範例中的,查詢會執行傳回資料,將會叫用 [FindProduct_Completed 方法。

(如 [圖 5 ] 所示),FindProduct_Completed 方法可接受的 IAsyncResult 參數,包含從查詢結果。 結果是藉由呼叫 EndExecute 方法,它會產生一組產品物件讀取的。 然後檢查每項產品,並的事件處理常式指派給其 PropertyChanged 事件]。 (在範例程式碼,我延伸 Product 類別使用部分類別加入實作 INotifyPropertyChanged)。 這個步驟可確保,在變更任何產品執行個體,在 Silverlight 時, 產品會藉由呼叫 UpdateObject 方法通知 DataServiceContext 的 (實體,在 [圖 5 )。 沒有這個的程式碼 DatServiceContext 就會很不知道在產品的執行個體,使用者所做的任何變更。 假設 _products 變數為型別 ObservableCollection <product> 並繫結至 DataGrid 控制項的 DataContext,產品將會出現在 DataGrid 控制項 (請參閱 [圖 6 )。

[圖 5 FindProduct_Completed 方法

private void FindProduct_Completed(IAsyncResult result)
{
    DataServiceQuery<Product> query = (DataServiceQuery<Product>)result.
         AsyncState;
    try
    {
        var entities = query.EndExecute(result);
        foreach (Product item in entities)
        {
            item.PropertyChanged += ((sender, e) =>
                                         {
                                             Product entity = (Product)
                                                                sender;
                                             _ctx.UpdateObject(entity);
                                         });
            _products.Add(item);
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Failed to retrieve data: " + ex.ToString());
    }
}

fig06.gif

[圖 6 載入 [產品] 和 [訂單詳細資訊 (Order Details)] 資料

延遲載入

在 Silverlight 在 ADO.NET 資料服務用戶端程式庫提供載入與已在 [DataServiceContext 物件相關聯的物件。 例如,上方 DataGrid 在該產品的訂單詳細資料可以繫結至較低的 DataGrid 之前, [圖 6 中, 所顯示的 Silverlight 控制項中選取產品時, 它們必須擷取。 在第一個步驟,在這個處理序中是指派給 productDataGrid 的 SelectionChanged 事件的事件處理常式。 然後選取產品時,事件處理常式 (如 [圖 7 ] 所示) 會使用 BeginLoadProperty 方法要求 OrderDetails 物件中取得選定產品的 ADO.NET 資料服務。

[圖 7 要求 Order Details 資料

void productDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Product product = productDataGrid.SelectedItem as Product;
    if (product == null) return;

    _orderDetails.Clear();

    if (product.OrderDetails == null || product.OrderDetails.Count == 0)
    {
        _ctx.BeginLoadProperty(product, "OrderDetails", FindOrderDetail_Completed, null);
    }
    else
    {
        LoadOrderDetails();
    }
}

BeginLoadProperty 方法提出的網路要求到取得 [OrderDetails 的記錄,並傳回時它將會叫用回呼方法,FindOrderDetail_Completed。 如果從這個程式碼傳遞至回呼 (Callback) 方法需要的任何其他狀態,它可以 BeginLoadProperty 方法的第四個參數中傳遞。 例如,相同的事件處理常式可能會用來接收從數個非同步查詢的結果。 值無法傳入狀態參數,以協助判斷它接收的非同步呼叫查詢結果的回呼方法。

當叫用回呼時,結果讀取到 DataServiceContext 物件使用其 EndLoadProperty) 方法,如下所示:

_ctx.EndLoadProperty(result);
Deployment.Current.Dispatcher.BeginInvoke(() => LoadOrderDetails());

然後,Order Details 資料會載入 LoadOrderDetails 方法。 因為程式碼執行的非同步作業完成結果時,並不保證會在 UI 執行緒上執行這個程式碼。

如果沒有在 UI 執行緒上執行程式碼,再順序的詳細資料的新集合不會出現在項目的 DataGrid。 基本上,任何 UI 作業,必須在 UI 執行緒上執行。 確保 UI 執行緒上執行的作業的方式之一是使用發送器物件的 BeginInvoke 方法。 上述程式碼使用發送器,以確定在 Order Details 資料在 UI 執行緒上載入藉由呼叫 LoadOrderDetails (如 [圖 8 ] 所示)。

[圖 8 載入順序的詳細資料

private void LoadOrderDetails()
{
    Product product = productDataGrid.SelectedItem as Product;
    if (product == null) return;

    var query = (from od in product.OrderDetails
                 orderby od.OrderID ascending
                 select od);
    foreach (OrderDetail item in query)
    {
        item.PropertyChanged += ((sender, e) =>
        {
            OrderDetail entity = (OrderDetail)sender;
            _ctx.UpdateObject(entity);
        });
        _orderDetails.Add(item);
    }
}

LoadOrderDetails 會擷取目前所選取的 Product 執行個體,並建立一個將會選取所有 OrderDetails 物件從選取的產品的 LINQ 查詢。 這是可能的因為在 Order Details 資料已載入 DataServiceContext 使用 EndLoadProperty。 在 Order Details 資料在 [圖 8 ] 中使用 LINQ 查詢來擷取後, 則每個 OrderDetail] 下物件執行個體會有其的 PropertyChanged 事件處理常式指派給 Lambda 運算式,告訴 DataServiceContext 是否會變更任何屬性值。 假設在 _orderDetails 物件是一個 ObservableCollection <orderdetail> 項目繫結至 orderDetailsDataGrid,訂單詳細資料會出現 (正如您所看到的中 [圖 6 )。

儲存資料

顯示的範例,也允許使用者儲存變更,產品和訂單詳細資料。 有兩個基本的步驟,以便儲存變更: 發生變更時通知 DataServiceContext 和發行儲存作業以非同步方式。

這些階層式資料集的每個會有一個部分類別,擴充 OrderDetail 實體 (Entity) 與產品的 Silverlight 中。 產品 (如 [圖 9 ] 所示) 的部分類別實作 INotifyPropertyChanged 介面,需要實作 PropertyChanged 事件。 藉由建立 ADO.NET 資料服務參考產生之部分產品類別服務會為每個公用屬性類別上,建立部分的方法。 每個屬性,取得當屬性已經變更時,屬性是有關變更和其中一個時,會引發的方法。 例如,Product 類別 ProductName 屬性) 都執行 OnProductNameChanging 部分方法] 和 [的 OnProductNameChanged 部分的方法。 圖 9 ] 顯示 [OnProductNameChanged 部分的 「 方法 (不產生的類別),自訂程式碼中會引發 PropertyChanged 事件。 這是用於追蹤所有實體的屬性值的所有變更,金鑰。 DataServiceContext 需要知道當屬性值變更時,這些變更為 ; 否則,它無法儲存變更。

[圖 9] 實作的變更告知

public partial class Product :INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void FirePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    partial void OnProductIDChanged() { FirePropertyChanged("ProductID"); }
    partial void OnProductNameChanged() { FirePropertyChanged("ProductName"); }
    partial void OnDiscontinuedChanged() { FirePropertyChanged("Discontinued"); }
    partial void OnDiscontinuedDateChanged() { FirePropertyChanged("DiscontinuedDate"); }
    partial void OnQuantityPerUnitChanged() { FirePropertyChanged("QuantityPerUnit"); }
    partial void OnReorderLevelChanged() { FirePropertyChanged("ReorderLevel"); }
    partial void OnRowVersionStampChanged() { FirePropertyChanged("RowVersionStamp"); }
    partial void OnUnitPriceChanged() { FirePropertyChanged("UnitPrice"); }
    partial void OnUnitsInStockChanged() { FirePropertyChanged("UnitsInStock"); }
    partial void OnUnitsOnOrderChanged() { FirePropertyChanged("UnitsOnOrder"); }
}

一旦在屬性變更事件處理常式會設定每個屬性,在 [DataServiceContext 不會進行注意的任何變更而其餘的工作,將資料儲存是相當簡單。 當使用者按一下 [儲存] 按鈕時則 BeginSaveChanges 叫用方法上 DataServiceContext,如下所示:

private void SaveButton_Clicked(object sender, RoutedEventArgs e)
{
    _ctx.BeginSaveChanges(SaveChangesOptions.Batch, 
        new AsyncCallback(Save_Complete), null);
}

這個方法會使透過 ADO.NET 資料服務,傳送的所有變更的 DataServiceContext 所知的 POST 作業。 讓回呼 (Callback) 方法才能處理結果,會以非同步方式,發出 POST。 在的 SaveChangesOption.batch 參數會指出應該以批次模式在交易中儲存所有變更。 如果有任何儲存作業失敗所有將會失效它們,並將會復原交易。 這項技術會運作是否您要儲存單一資料錄從一個實體] 或 [從多個關聯的實體的多個資料錄。

預先擷取資料

稍早有討論如何執行延遲載入抓取現有實體的記錄,然後將附加至 DataServiceContext 中使用的 [DataServiceContext BeginLoadProperty 方法。 這項技巧非常不一定需要資料時,特別是,視需要,取得額外的資料中使用。 延遲的載入儲存的擷取使用者可能不想要資料請參魷 \ cs6 \ f1 \ cf6 \ lang1024,除非使用者要求資料 (發生在使用者在 productDataGrid 控制項中選取產品時) 的成本。

正在擷取階層式資料的另一個技巧,是所有預先要求。 例如,擷取產品資料錄時它可能也會取得每項產品的分類和供應商有幫助。 否則,分類和產品執行個體的 [供應商] 屬性會是 null。 BeginLoadProperty 技術無法被使用,但是,需要進行許多網路要求]。 一個更好的技術,一次取得的所有資料是在 LINQ 查詢,使用展開方法,因為它需要只單一的 HTTP 要求。 下列查詢會示範要求所有的類別和供應商在 LINQ 查詢中選取產品的展開方法。

   DataServiceQuery<Product> dq = (
    from p in _ctx.ProductSet.Expand("Categories").Expand("Suppliers")
    select p) 
    as DataServiceQuery<Product>;

展開方法會是有代價,它會擷取其他資料。 這只有在需要以擷取最多前的所有資料時,才應該使用。 在上述的程式碼中,展開方法會擷取每個產品的實體的子資料錄。 也就是說,類別] 和 [供應商都是產品的兩個直接子系。 請注意,屬性,無法在實體集,名稱的名稱傳遞至展開方法。

如果需要多個層級的階層太取得這些記錄適應展開方法的語法。 例如,如果要抓取 OrderDetail 每項產品,以及這些 OrderDetail 的訂單中每個項目,語法可能如下所示:

DataServiceQuery<Product> dq = (
    from p in _ctx.ProductSet.Expand("OrderDetails/Orders")
    select p) 
    as DataServiceQuery<Product>;

此代碼表示查詢應該取得訂單 OrderDetails 屬性的每項產品,以及每個實體實體 (Entity)。(Product 類別具有 OrderDetails 屬性並 OrderDetail 類別具有訂單屬性)。 因為它們需要取得每項產品的訂單屬性時,會隱含 OrderDetail 記錄。這項查詢會將超過 8MB XML 資料的返回 Silverlight 的用戶端應用程式。這會是大量資料,而且可能會慢速連線上的效能負面影響。我建議您只在需要資料時,請使用展開方法並甚至會在這些情況中,使用以最嚴格的篩選條件可以避免在擷取的資料,您不需要。

若要將無法繼續

有是組合的 ADO.NET 資料服務和 Silverlight 進行穩固的資料導向應用程式所公開功能的位元相當。在為未來資料點的我希望重新瀏覽該主題,並提供更多的秘訣。現在,取出我的部落格):johnpapa。netSilverlight。網路網站和資料點的前一個 installments更多的 Silverlight 資料-中心應用程式。

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

John Papa (johnpapa。net) 是 ASPSOFT 與棒球的愛好者為資深顧問,人員花與他的家人 Kadi 的夏天住宿。John、 C# MVP,Silverlight Insider 和 INETA 的喇叭已撰寫包括他最新標題的 Data-Driven Services with Silverlight 2 (O ' Reilly,2009) 幾本書籍。將說出而他通常呈在混合、 DevConnections,和 VSLive 的會議。