LINQ 投影 (select new { ... }) 生成匿名類型,導致無法作為 WCF 服務操作返回類型、無法跨方法邊界傳遞。WCF Data Services 4.0 引入 $select 查詢選項支援投影,但自託管 WCF / RIA Services 仍需強類型方案。本文對比三種方案,重點講解 Entity Framework QueryView (映射視圖) 實現編譯期強類型投影,完美解決「臨時視圖類型」難題。
// 無法作為服務操作返回類型
public IQueryable<object> GetCustomers()
{
return ctx.Customers.Select(c => new { c.CustomerID, c.LastName }); // 編譯錯誤
}
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 定義專用 DTO 類 | 強類型、可序列化、可跨層傳遞 | 類爆炸、維護成本高、一次性視圖仍需寫類 | 穩定、復用的投影 |
| WCF Data Services $select | 無需服務端代碼、客戶端動態投影 | 僅限 OData 端點、客戶端需支援 LINQ to REST | OData 服務、Silverlight/JS 客戶端 |
| EF QueryView (映射視圖) | 模型級強類型、無新類、可更新、跨 WCF/RIA 通用 | 需手寫 EDMX XML、不支援投影內字符串拼接 | 參數選用表、只讀引用列表、頻繁復用的投影 |
<EntityType Name="CustomerNameAndID">
<Key><PropertyRef Name="CustomerID" /></Key>
<Property Name="CustomerID" Type="Int32" Nullable="false" />
<Property Name="FirstName" Type="String" Nullable="false" MaxLength="50" />
<Property Name="LastName" Type="String" Nullable="false" MaxLength="50" />
</EntityType>
<EntityType Name="Customer"> ... <Property Name="CustomerID" Type="int" ... /> ... </EntityType>
<EntityContainer Name="ModelStoreContainer"> ... </EntityContainer>
<EntityContainerMapping ...>
<EntitySetMapping Name="CustomerNameAndIDs">
<EntityTypeMapping TypeName="AWModel.CustomerNameAndID">
<QueryView>
SELECT VALUE AWModel.CustomerNameAndID(c.CustomerID, c.FirstName, c.LastName)
FROM ModelStoreContainer.Customer AS c
</QueryView>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
關鍵點:SELECT VALUE 構造實體,參數順序與屬性順序嚴格對應;不支援 投影內拼接 (c.FirstName + ' ' + c.LastName),需客戶端處理。
// WCF 服務
public List<CustomerNameAndID> GetCustomerPickList()
{
using (var ctx = new AWEntities())
{
return ctx.CustomerNameAndIDs.OrderBy(c => c.LastName).ToList();
}
}
// RIA Services 域服務
public IQueryable<RestaurantPickList> GetRestaurantPickList()
{
return context.RestaurantPickLists; // 同樣受益於 QueryView
}
// WCF Data Services 客戶端
var list = ctx.CustomerNameAndIDs.ToList(); // 無投影代碼
Insert/Update/Delete 存儲過程實現寫入。ObjectContext 正常跟蹤實體狀態,SaveChanges 可持久化 (若映射了修改存儲過程)。CustomerNameAndIDs) 需與 MSL 中 EntitySetMapping Name 一致。QueryView 將「投影視圖」提升為模型一等公民,消除 DTO 類爆炸,實現編譯期類型安全與跨層傳遞。對於參數選用表、引用數據、只讀報表等高頻投影場景,是比匿名類型與手寫 DTO 更優雅的架構選擇。