前言
閱讀本文之前,您也可以到Asp.Net Web API 2 系列導(dǎo)航進(jìn)行查看http://www.survivalescaperooms.com/aehyok/p/3446289.html。
本文的示例代碼的下載地址為http://pan.baidu.com/s/1o6lqXN8
大多數(shù)的數(shù)據(jù)集定義實(shí)體間的關(guān)系:客戶(hù)有訂單、書(shū)籍有作者、產(chǎn)品有供應(yīng)商。客戶(hù)端可以使用OData操作實(shí)體間的關(guān)系。給定一個(gè)產(chǎn)品,你可以找到該產(chǎn)品的供應(yīng)商。您也可以創(chuàng)建或者刪除關(guān)系。例如,您也可以為一個(gè)產(chǎn)品設(shè)置一個(gè)供應(yīng)商。
本教程將會(huì)展示在A(yíng)sp.Net Web API中支持這些操作。本文的教程是建立在上一節(jié)的教程之上http://www.survivalescaperooms.com/aehyok/p/3545824.html。
Add a Supplier Entity添加一個(gè)供應(yīng)商實(shí)體類(lèi)
首先我們需要來(lái)添加一個(gè)Supplier的實(shí)體類(lèi)
namespace OData.Models{ public class Supplier { [Key] public string Key { get; set; } public string Name { get; set; } }}
這個(gè)類(lèi)使用了一個(gè)字符串類(lèi)型的實(shí)體鍵。在實(shí)踐中,這可能比使用整形鍵不太常見(jiàn)的。但它是值得的看到OData如何處理除了整數(shù)以外的其他鍵類(lèi)型。
接下來(lái),我們將通過(guò)在PRoduct類(lèi)上添加一個(gè)Supplier的屬性來(lái)建立一個(gè)關(guān)系。
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; } }
添加一個(gè)新的DbSet到ProductServiceContext
類(lèi),從而使實(shí)體框架將包括Supplier在數(shù)據(jù)庫(kù)表中。
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,添加一個(gè)“Suppliers”實(shí)體的EDM模型:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Product>("Products"); // New code: builder.EntitySet<Supplier>("Suppliers");
Navigation Properties導(dǎo)航屬性
為了得到一個(gè)產(chǎn)品的供應(yīng)商,客戶(hù)端發(fā)送了一個(gè)Get請(qǐng)求:
GET /Products(1)/Supplier
在Product類(lèi)型上有一個(gè)Supplier的導(dǎo)航屬性。在這個(gè)實(shí)例中,Supplier是一個(gè)單一的項(xiàng)。但是一個(gè)導(dǎo)航屬性也能返回一個(gè)集合(一對(duì)多或者多對(duì)多的 關(guān)系)。
為了支持這個(gè)請(qǐng)求,在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這個(gè)參數(shù)就是這個(gè)Product的鍵。這個(gè)方法返回關(guān)聯(lián)的實(shí)體——在這個(gè)實(shí)例中,就是一個(gè)Supplier對(duì)象。方法的名稱(chēng)和參數(shù)的名稱(chēng)都是非常重要的。總之,如果導(dǎo)航屬性被命名為一個(gè)“X”,你需要添加一個(gè)被命名為“GetX”的方法。這個(gè)方法必須采用一個(gè)命名為“key”的參數(shù),用來(lái)匹配父類(lèi)數(shù)據(jù)類(lèi)型的key。
它也是很重要的在鍵參數(shù)上擁有【FromOdataUri】的屬性。當(dāng)它從請(qǐng)求的URL中解析鍵時(shí),這個(gè)屬性將會(huì)告訴Web API去使用Odata語(yǔ)法規(guī)則。
Creating and Deleting Links
OData支持創(chuàng)建和刪除兩個(gè)實(shí)體之間的關(guān)系。在OData術(shù)語(yǔ)中,這個(gè)關(guān)系就是一個(gè)“link”。每個(gè)link有一個(gè)攜帶entity/$links/entity的Url。例如,由產(chǎn)品到供應(yīng)商的鏈接看起來(lái)像這樣:
/Products(1)/$links/Supplier
為了創(chuàng)建一個(gè)新的鏈接,這個(gè)客戶(hù)端發(fā)送了一個(gè)post請(qǐng)求到這個(gè)鏈接URI。請(qǐng)求的消息體就是目標(biāo)實(shí)體的URI。例如,假設(shè)有一個(gè)供應(yīng)商的鍵為“CTSO”。為了創(chuàng)建一個(gè)鏈接由“Product(1)”到”Supplier('CTSO')“,客戶(hù)端發(fā)送一個(gè)請(qǐng)求如下:
POST http://localhost/odata/Products(1)/$links/SupplierContent-Type: application/jsonContent-Length: 50{"url":"http://localhost/odata/Suppliers('CTSO')"}
對(duì)于刪除一個(gè)鏈接,客戶(hù)端發(fā)送了一個(gè)DELETE 請(qǐng)求到鏈接URI。
Creating Links
為啟用一個(gè)客戶(hù)端去創(chuàng)建產(chǎn)品-供應(yīng)商的鏈接,需要在ProductsController類(lèi)中添加如下的代碼:
[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(); }}
這個(gè)方法有三個(gè)參數(shù):
第一個(gè)key:就是引導(dǎo)到父類(lèi)實(shí)體的鍵
第二個(gè)navigationProperty: 導(dǎo)航屬性的名稱(chēng)。例如,最合適的導(dǎo)航屬性Supplier。
第三個(gè)link:被鏈接實(shí)體的OData的URI。這個(gè)值是從消息體中獲得。例如,這個(gè)鏈接URI可能是”http://localhost/odata/Suppliers('CTSO')“,也就是供應(yīng)商中有ID="CTSO"。
這個(gè)方法用這個(gè)鏈接去查找Supplier。如果匹配的供應(yīng)商被發(fā)現(xiàn),這個(gè)方法將會(huì)設(shè)置Product實(shí)體類(lèi)的Supplier的屬性,并且保存結(jié)果到數(shù)據(jù)庫(kù)。
其中最難的部分是解析鏈接URI。從根本上來(lái)說(shuō),你需要模擬發(fā)送一個(gè)get請(qǐng)求到那個(gè)URI。接下來(lái)的輔助方法將會(huì)展示如何處理它。這個(gè)方法調(diào)用Web API路由過(guò)程,返回一個(gè)OData實(shí)體,展現(xiàn)被轉(zhuǎn)換的OData路徑。對(duì)于一個(gè)鏈接URI,這個(gè)片段數(shù)中應(yīng)該有一個(gè)實(shí)體鍵。
// 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
對(duì)于刪除一個(gè)鏈接,在ProductsController類(lèi)中添加如下代碼:
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(); }}
在這個(gè)例子中,這個(gè)導(dǎo)航屬性是一個(gè)簡(jiǎn)單的Supplier實(shí)體。如果導(dǎo)航屬性是一個(gè)集合,對(duì)于刪除一個(gè)鏈接的URI必須在被關(guān)聯(lián)的實(shí)體中有一個(gè)鍵。例如:
DELETE /odata/Customers(1)/$links/Orders(1)
這里展示的則是1對(duì)多的關(guān)系中,刪除其中的一個(gè)的例子。
這個(gè)請(qǐng)求就是從客戶(hù)1中移除訂單為1的。這個(gè)DeleteLink方法將會(huì)有如下簽名:
void DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty);
簡(jiǎn)單測(cè)試結(jié)果
1、http://localhost:3629/Odata/Products(1)/Supplier
2、
將ID=2的Supplier修改為WING
請(qǐng)求Header
POST http://localhost/odata/Products(2)/$links/SupplierContent-Type: application/jsonContent-Length: 50
請(qǐng)求Body
{"url":"http://localhost/odata/Suppliers('WING')"}
現(xiàn)在再次查看http://localhost/Odata/Products
3、DELETE http://localhost/odata/Products(2)/$links/Supplier那么這樣就可以將上面的SupplierId=WING修改為null
然后再次執(zhí)行http://localhost/Odata/Products查看
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注