前言
閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html。
本文的示例代碼的下載地址為http://pan.baidu.com/s/1o6lqXN8
大多數的數據集定義實體間的關系:客戶有訂單、書籍有作者、產品有供應商。客戶端可以使用OData操作實體間的關系。給定一個產品,你可以找到該產品的供應商。您也可以創建或者刪除關系。例如,您也可以為一個產品設置一個供應商。
本教程將會展示在Asp.Net Web API中支持這些操作。本文的教程是建立在上一節的教程之上http://www.cnblogs.com/aehyok/p/3545824.html。
Add a Supplier Entity添加一個供應商實體類
首先我們需要來添加一個Supplier的實體類
namespace OData.Models { public class Supplier { [Key] public string Key { get; set; } public string Name { get; set; } } }
這個類使用了一個字符串類型的實體鍵。在實踐中,這可能比使用整形鍵不太常見的。但它是值得的看到OData如何處理除了整數以外的其他鍵類型。
接下來,我們將通過在Product類上添加一個Supplier的屬性來建立一個關系。
public class Product { public int ID { get; set; } public string Name { get; set; } public decimal Price { get; set; } public string Category { get; set; } // New code [ForeignKey("Supplier")] public string SupplierId { get; set; } public virtual Supplier Supplier { get; set; } }
添加一個新的DbSet到ProductServiceContext
類,從而使實體框架將包括Supplier在數據庫表中。
public class ProductServiceContext : DbContext { public ProductServiceContext() : base("name=ProductServiceContext") { } public DbSet<Product> Products { get; set; } ///New Code public DbSet<Supplier> Suppliers { get; set; } }
在WebApiConfig.cs,添加一個“Suppliers”實體的EDM模型:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Product>("Products"); // New code: builder.EntitySet<Supplier>("Suppliers");
Navigation Properties導航屬性
為了得到一個產品的供應商,客戶端發送了一個Get請求:
GET /Products(1)/Supplier
在Product類型上有一個Supplier的導航屬性。在這個實例中,Supplier是一個單一的項。但是一個導航屬性也能返回一個集合(一對多或者多對多的 關系)。
為了支持這個請求,在ProductsController上添加如下方法:
// GET /Products(1)/Supplier public Supplier GetSupplier([FromODataUri] int key) { Product product = db.Products.FirstOrDefault(p => p.ID == key); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return product.Supplier; }
key這個參數就是這個Product的鍵。這個方法返回關聯的實體——在這個實例中,就是一個Supplier對象。方法的名稱和參數的名稱都是非常重要的。總之,如果導航屬性被命名為一個“X”,你需要添加一個被命名為“GetX”的方法。這個方法必須采用一個命名為“key”的參數,用來匹配父類數據類型的key。
它也是很重要的在鍵參數上擁有【FromOdataUri】的屬性。當它從請求的URL中解析鍵時,這個屬性將會告訴Web API去使用Odata語法規則。
Creating and Deleting Links
OData支持創建和刪除兩個實體之間的關系。在OData術語中,這個關系就是一個“link”。每個link有一個攜帶entity/$links/entity的Url。例如,由產品到供應商的鏈接看起來像這樣:
/Products(1)/$links/Supplier
為了創建一個新的鏈接,這個客戶端發送了一個post請求到這個鏈接URI。請求的消息體就是目標實體的URI。例如,假設有一個供應商的鍵為“CTSO”。為了創建一個鏈接由“Product(1)”到”Supplier('CTSO')“,客戶端發送一個請求如下:
POST http://localhost/odata/Products(1)/$links/Supplier Content-Type: application/json Content-Length: 50
{"url":"http://localhost/odata/Suppliers('CTSO')"}
對於刪除一個鏈接,客戶端發送了一個DELETE 請求到鏈接URI。
Creating Links
為啟用一個客戶端去創建產品-供應商的鏈接,需要在ProductsController類中添加如下的代碼:
[AcceptVerbs("POST", "PUT")] public async Task<IHttpActionResult> CreateLink([FromODataUri] int key, string navigationProperty, [FromBody] Uri link) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Product product = await db.Products.FindAsync(key); if (product == null) { return NotFound(); } switch (navigationProperty) { case "Supplier": string supplierKey = GetKeyFromLinkUri<string>(link); Supplier supplier = await db.Suppliers.FindAsync(supplierKey); if (supplier == null) { return NotFound(); } product.Supplier = supplier; await db.SaveChangesAsync(); return StatusCode(HttpStatusCode.NoContent); default: return NotFound(); } }
這個方法有三個參數:
第一個key:就是引導到父類實體的鍵
第二個navigationProperty: 導航屬性的名稱。例如,最合適的導航屬性Supplier。
第三個link:被鏈接實體的OData的URI。這個值是從消息體中獲得。例如,這個鏈接URI可能是”http://localhost/odata/Suppliers('CTSO')“,也就是供應商中有ID="CTSO"。
這個方法用這個鏈接去查找Supplier。如果匹配的供應商被發現,這個方法將會設置Product實體類的Supplier的屬性,並且保存結果到數據庫。
其中最難的部分是解析鏈接URI。從根本上來說,你需要模擬發送一個get請求到那個URI。接下來的輔助方法將會展示如何處理它。這個方法調用Web API路由過程,返回一個OData實體,展現被轉換的OData路徑。對於一個鏈接URI,這個片段數中應該有一個實體鍵。
// Helper method to extract the key from an OData link URI. private TKey GetKeyFromLinkUri<TKey>(Uri link) { TKey key = default(TKey); // Get the route that was used for this request. IHttpRoute route = Request.GetRouteData().Route; // Create an equivalent self-hosted route. IHttpRoute newRoute = new HttpRoute(route.RouteTemplate, new HttpRouteValueDictionary(route.Defaults), new HttpRouteValueDictionary(route.Constraints), new HttpRouteValueDictionary(route.DataTokens), route.Handler); // Create a fake GET request for the link URI. var tmpRequest = new HttpRequestMessage(HttpMethod.Get, link); // Send this request through the routing process. var routeData = newRoute.GetRouteData( Request.GetConfiguration().VirtualPathRoot, tmpRequest); // If the GET request matches the route, use the path segments to find the key. if (routeData != null) { ODataPath path = tmpRequest.GetODataPath(); var segment = path.Segments.OfType<KeyValuePathSegment>().FirstOrDefault(); if (segment != null) { // Convert the segment into the key type. key = (TKey)ODataUriUtils.ConvertFromUriLiteral( segment.Value, ODataVersion.V3); } } return key; }
Deleting Links
對於刪除一個鏈接,在ProductsController類中添加如下代碼:
public async Task<IHttpActionResult> DeleteLink([FromODataUri] int key, string navigationProperty) { Product product = await db.Products.FindAsync(key); if (product == null) { return NotFound(); } switch (navigationProperty) { case "Supplier": product.Supplier = null; await db.SaveChangesAsync(); return StatusCode(HttpStatusCode.NoContent); default: return NotFound(); } }
在這個例子中,這個導航屬性是一個簡單的Supplier實體。如果導航屬性是一個集合,對於刪除一個鏈接的URI必須在被關聯的實體中有一個鍵。例如:
DELETE /odata/Customers(1)/$links/Orders(1)
這里展示的則是1對多的關系中,刪除其中的一個的例子。
這個請求就是從客戶1中移除訂單為1的。這個DeleteLink方法將會有如下簽名:
void DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty);
簡單測試結果
1、http://localhost:3629/Odata/Products(1)/Supplier
2、
將ID=2的Supplier修改為WING
請求Header
POST http://localhost/odata/Products(2)/$links/Supplier Content-Type: application/json Content-Length: 50
請求Body
{"url":"http://localhost/odata/Suppliers('WING')"}
現在再次查看http://localhost/Odata/Products
3、DELETE http://localhost/odata/Products(2)/$links/Supplier那么這樣就可以將上面的SupplierId=WING修改為null
然后再次執行http://localhost/Odata/Products查看
總結
本文所參考鏈接為http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations
本文示例代碼下載地址為http://pan.baidu.com/s/1o6lqXN8