程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 數據點-WCF服務中的LINQ投影查詢和替代方案

數據點-WCF服務中的LINQ投影查詢和替代方案

編輯:關於.NET

上個月當我的本地 .NET 用戶組的演示者正在課堂上寫 LINQ 查詢時,我問他 ,“以前沒有 LINQ 的時候,我們是怎麼過的”?他回答說,“真是難以想象” 。

這是真的。自從 2008 年 LINQ 被引入 Visual Studio 後,它對我們在 Microsoft .NET Framework 中的編程方式產生了如此 重大的影響。與 Visual Basic 和 C# 中引入的許多新的語言功能相結合,LINQ 可以前後一致地解決查 詢內存中對象和數據來源的問題。

LINQ 具備將形狀隨機的數據投影到匿名類型的功能,這種功能既有好的一面 ,也有不好的一面。如果您只需要獲取數據的特定視圖,而不必為此一次性的類 型聲明新類,匿名類型是一個不錯的解決方案。LINQ 的投影和匿名類型的確是把 我們寵壞了。那麼,為什麼我說,它們也有不好的一面呢?

如果您曾經將 LINQ 投影用於需要將數據返回另一方法的某種方法,或者更糟 糕,將 LINQ 投影用在 Windows Communication Foundation (WCF) 服務操作中 ,對此您可能會有所了解。

原因即在於匿名類型是一次性類型,它們沒有聲明,只有創建它們的方法可以 理解它們。如果您寫了一個返回一列匿名類型的查詢,因為沒有辦法表達“匿名 類型”,所以沒有辦法定義某個方法參數,說:“我將返回一列...”。

以下是一個采用簡單投影的 LINQ to Entities 查詢:

var custQuery = from c in context.Customers

          select new {c.CustomerID,  Name=c.LastName.Trim() +

          ", " + c.FirstName};

在運行時,custQuery 變量將實際成為某個 ObjectQuery<<>f__AnonymousType0<int,string>>。

有了 var(以及 Visual Basic Dim 的替代使用)我們不再需要找到這種非類 型的表達方式。

如果您想從某個方法返回該查詢的結果,唯一合理的解決方案是創建代表要返 回的類型的類。不過,這樣做,提供匿名類型將毫無意義。現在您必須寫更多的 代碼,定義類,還可能需要定義容納新類的新項目,並確保使用這些類的程序集 能訪問到它們等等。

最近,數據服務又給出了一道難題。為了對數據進行投影,您必須在服務中創 建自定義的操作,執行自己的查詢,然後返回某一預先定義的、可以為客戶端理 解的類。

在您處理服務時,很多情況下您都希望在無需通過線路移動大規模類型的情況 下,處理數據的特定視圖。

為了滿足這一臨時性要求,除了在您的域中創建額外的類型之外,您還有更多 的選擇。

WCF 數據服務中的新投影功能

.NET Framework 3.5 SP1 的數據服務更新為 WCF 數據服務引入了少數幾個強 大的功能,這也是 .NET Framework 4 的組成部分。這些功能中就有針對數據服 務在查詢中使用投影的功能。強烈建議您查看 WCF 數據服務團隊的博客帖子 (blogs.msdn.com/astoriateam/archive/2010/01/27/data-services-update- for-net-3-5-sp1-available-for-download.aspx),以了解這次更新的所有新功 能。

數據服務 URI 語法中添加了 $select 運算符。它允許使用屬性甚至是導航屬 性投影。

下面簡單舉例說明了隨 SalesOrderHeaders 導航屬性獲取客戶的幾個標量屬 性的投影:

http://localhost /DataService.svc/Customers(609)
  $select=CustomerID,LastName,FirstName,SalesOrderHeaders&$expand=
  SalesOrderHeaders

擴展運算符強制結果不僅包含到這些訂單的鏈接,還包含每個訂單的數據。

圖 1 顯示了此查詢的結果。擴展的 SalesOrderHeaders(只包含一個訂單) 以黃色突出顯示,而客戶信息以綠色突出顯示。

圖 1 請求三個客戶屬性和客戶的 SalesOrderHeaders 的數據服務查詢投影的 結果

.NET Framework 中的 LINQ to REST 功能以及 WCF 數據服務的 Silverlight 客戶端 API 已得到了更新,也允許使用投影:

var projectedCust = (from c in context.Customers

           where c.CustomerID==609 

           select new {c.CustomerID, c.LastName})

           .FirstOrDefault();

ProjectedCust 現在是一個可供用於客戶端應用程序的匿名類型。

它還可能投影到已知實體類型,而且在某些情況下,DataContext 可以跟蹤由 客戶端所做的更改,這些更改還可以通過服務的 SaveChanges 方法保留下來。請 注意,任何缺少的屬性將被填充其默認值(或填充為空,如果它們可為空值), 並保留到數據庫。

自 EDM 啟用投影強類型

如果您使用實體框架實體數據模型 (EDM),可使用某種方便易用的方法,在您 需要從創建匿名類型的方法中將它們傳遞出時避免被卡住。

EDM 有名為 QueryView 的映射。我過去在提供數據服務投影支持前,曾向很 多客戶指明這一點。它不僅可以為數據服務很好地解決這個問題,還能為自定義 WCF 服務和 RIA 服務解決問題。

什麼是 QueryView?這是實體框架元數據中的一種特殊映射類型。如圖 2 所 示,通常,您會根據元數據的存儲模型存儲架構定義語言 (SSDL) 的說明,將實 體的屬性映射到數據庫表或視圖列。

圖 2 直接將表列映射到實體屬性

與此相反,QueryView 會讓您通過這些 SSDL 表列創建視圖,而不是直接映射 到它們。使用 QueryView 有多個理由。一些例子包括:采用只讀方式公開實體, 以條件映射不允許的方式過濾實體,或提供數據庫中數據表的不同視圖。

針對上述目的的最後一個,我將重點關注您經常會在自己的應用程序中投影的 匿名類型的替代方案。參數選用表即是其中一個例子。為什麼要為只需要 ID 和 客戶姓名的下拉菜單返回全部客戶類型?

生成 QueryView

創建 QueryView 之前,您需要在模型中創建代表您所針對的視圖形狀的實體 ,如 CustomerNameAndID 實體。

但是您不能將這個實體直接映射到 SSDL 中的 Customer 表。將 Customer 實 體和 CustomerNameAndID 實體都映射到表的 CustomerID 列會產生沖突。

正如您可以在數據庫中創建表視圖,您可以改為直接在元數據中創建 SSDL Customer 視圖。QueryView 就是 SSDL 上的 Entity SQL 表達式。它是模型的映 射規范語言 (MSL) 元數據的一部分。創建 QueryView 時無法獲得設計器支持, 所以您需要直接在 XML 中鍵入。

因為您要映射到表的存儲架構,所以最好看看其外觀。圖 3 列出了 Customer 數據庫表的 SSDL 描述,除了使用了提供商數據類型之外,它與概念模型的元數 據的 Customer 實體類似。

圖 3 數據庫 Customer 表的 SSDL 描述

<EntityType Name="Customer">

  <Key>

   <PropertyRef Name="CustomerID" />

  </Key>

  <Property Name="CustomerID" Type="int"  Nullable="false"

       StoreGeneratedPattern="Identity" />

  <Property Name="Title" Type="nvarchar"  MaxLength="8" />

  <Property Name="FirstName" Type="nvarchar"  Nullable="false"

       MaxLength="50" />

  <Property Name="MiddleName" Type="nvarchar"  MaxLength="50" />

  <Property Name="LastName" Type="nvarchar"  Nullable="false"

       MaxLength="50" />

  <Property Name="Suffix" Type="nvarchar"  MaxLength="10" />

  <Property Name="CompanyName" Type="nvarchar" MaxLength="128"  />

  <Property Name="SalesPerson" Type="nvarchar" MaxLength="256"  />

  <Property Name="EmailAddress" Type="nvarchar" MaxLength="50"  />

  <Property Name="Phone" Type="nvarchar"  MaxLength="25" />

  <Property Name="ModifiedDate" Type="datetime"  Nullable="false" />

  <Property Name="TimeStamp" Type="timestamp"  Nullable="false"

       StoreGeneratedPattern="Computed" />

</EntityType>

存儲架構的命名空間 ModelStoreContainer 是 QueryView 的另一個重要元素 。現在,您已經擁有了構建 QueryView 表達式的所有必要元件。以下 QueryView 將三個必填字段從 SSDL 投影到我在模型中創建的 CustomerNameAndID 實體:

SELECT VALUE AWModel.CustomerNameAndID(c.CustomerID,  c.FirstName, 

     c.LastName) FROM ModelStoreContainer.Customer as  c

我們來描述下該實體 SQL:“查詢存儲架構中的客戶,取出這三列,讓他們作 為 CustomerNameAndID 實體返回給我。”AWModel 是概念模型的實體容器的命名 空間。對於表達式中引用的概念架構定義語言 (CSDL) 和 SSDL 類型,您需要使 用其強類型化名稱。

只要投影的結果(整數、字符串與字符串)與目標實體的架構相匹配,映射將 成功。我試過在投影內使用函數和連接(如 (c.CustomerID, c.FirstName + c.LastName)),但沒有成功,並收到了不允許使用這些函數的錯誤消息。因此, 我被迫使用 FirstName 和 LastName 屬性,而讓客戶端處理連接問題。

將 QueryView 置入元數據

對於進入 EntityContainerMapping 的實體,您必須將 EntitySetMapping 元 素內的 QueryView 表達式置入元數據。圖 4 在我的 EDMX 文件的原始 XML 中顯 示了這一 QueryView(以黃色突出顯示)。

圖 4 映射部分的 QueryView

現在我的 CustomerNameAndID 已成為我的模型的一部分,可提供給任何消費 者。此 QueryView 還有另一優勢。盡管這一 QueryView 的目標是創建只讀引用 列表,您還可以使用 QueryView 更新所映射的實體。上下文將跟蹤 CustomerNameAndID 對象的變化。雖然實體框架不能夠為這一實體自動生成插入 、更新和刪除命令,您可以將存儲過程映射到它。

受惠於 QueryView

既然您已經在模型中加入了 QueryView,您就不再需要依靠投影或匿名類型來 檢索數據的這些視圖。如下所示,在 WCF Data Services 中, CustomerNameAndIDs 將成為可供查詢的有效實體集:

List<CustomerNameAndID> custPickList =

  context.CustomerNameAndIDs.ToList();

再沒有雜亂的投影了;更有優勢的一點是,您不需要在應用程序中定義新的類 型,並投影到它們,即可在自定義 WCF 服務中創建現在可以返回此強類型化對象 的服務操作。

public List<CustomerNameAndID> GetCustomerPickList()

   {

    using (var context = new AWEntities())

    {

     return context.CustomerNameAndIDs.OrderBy(

      c => c.LastName).ToList();

    }

   }

因受到限制,我們無法連接 QueryView 中的名字和姓氏,要由使用此服務的 開發人員一方實現這個連接。

WCF RIA 服務也可受益於 QueryView。您可能希望公開某種方法,從您的域服 務中檢索某家餐廳的參數選用表。您不必在域服務中創建額外的類來表示這些投 影的屬性,該 RestaurantPickList 實體可獲得模型中的 QueryView 支持,輕松 提供這一數據:

public IQueryable<RestaurantPickList>  GetRestaurantPickList()

   {

    return context.RestaurantPickLists;

   }

是選擇 QueryViews 還是投影,我們已經算面面俱到了

有能力通過您的數據類型對視圖進行投影是查詢方面的巨大優勢,這是 WCF 數據服務增加的一項很不錯的功能。即便如此,如果有時不需要進行投影,也不 必擔心結果共享,即可訪問這些視圖,將簡化您的某些編碼任務。

最後一個注意事項:隨著在實體框架的 .NET Framework 4 版本中引入外鍵, 因為您可以返回只讀實體,並輕松使用它們的屬性來更新正在編輯的實體中的外 鍵屬性,QueryView 參數選用表將更有意義。

下載示例代碼:http://code.msdn.microsoft.com/mag201005DataPoints

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved