Asp.Net Web API 2第十八課——Working with Entity Relations in OData


前言

  閱讀本文之前,您也可以到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; }
    }

添加一個新DbSetProductServiceContext類,從而使實體框架將包括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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM