Bagikan melalui


Mendukung Hubungan Entitas di OData v3 dengan Web API 2

oleh Mike Wasson

Unduh Proyek yang Selesai

Sebagian besar himpunan data mendefinisikan hubungan antar entitas: Pelanggan memiliki pesanan; buku memiliki penulis; produk memiliki pemasok. Dengan menggunakan OData, klien dapat menavigasi melalui hubungan entitas. Mengingat produk, Anda dapat menemukan pemasok. Anda juga dapat membuat atau menghapus hubungan. Misalnya, Anda dapat mengatur pemasok untuk produk.

Tutorial ini menunjukkan cara mendukung operasi ini di ASP.NET Web API. Tutorial ini dibangun pada tutorial Membuat Titik Akhir OData v3 dengan Web API 2.

Versi perangkat lunak yang digunakan dalam tutorial

  • WEB API 2
  • OData Versi 3
  • Entity Framework 6

Menambahkan Entitas Pemasok

Pertama, kita perlu menambahkan jenis entitas baru ke umpan OData kita. Kami akan menambahkan Supplier kelas.

using System.ComponentModel.DataAnnotations;

namespace ProductService.Models
{
    public class Supplier
    {
        [Key]
        public string Key { get; set; }
        public string Name { get; set; }
    }
}

Kelas ini menggunakan string untuk kunci entitas. Dalam praktiknya, itu mungkin kurang umum daripada menggunakan kunci bilangan bulat. Tetapi ada baiknya melihat bagaimana OData menangani jenis kunci lain selain bilangan bulat.

Selanjutnya, kita akan membuat hubungan dengan menambahkan Supplier properti ke Product kelas :

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; }
}

Tambahkan DbSet baru ke ProductServiceContext kelas , sehingga Kerangka Kerja Entitas akan menyertakan Supplier tabel dalam database.

public class ProductServiceContext : DbContext
{
    public ProductServiceContext() : base("name=ProductServiceContext")
    {
    }

    public System.Data.Entity.DbSet<ProductService.Models.Product> Products { get; set; }
    // New code:
    public System.Data.Entity.DbSet<ProductService.Models.Supplier> Suppliers { get; set; }
}

Di WebApiConfig.cs, tambahkan entitas "Pemasok" ke model EDM:

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
// New code:
builder.EntitySet<Supplier>("Suppliers");

Untuk mendapatkan pemasok produk, klien mengirimkan permintaan GET:

GET /Products(1)/Supplier

Di sini "Pemasok" adalah properti navigasi pada jenis .Product Dalam hal ini, Supplier mengacu pada satu item, tetapi properti navigasi juga dapat mengembalikan koleksi (relasi satu-ke-banyak atau banyak-ke-banyak).

Untuk mendukung permintaan ini, tambahkan metode berikut ke ProductsController kelas :

// GET /Products(1)/Supplier
public Supplier GetSupplier([FromODataUri] int key)
{
    Product product = _context.Products.FirstOrDefault(p => p.ID == key);
    if (product == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return product.Supplier;
}

Parameter kunci adalah kunci produk. Metode mengembalikan entitas terkait—dalam hal ini, instans Supplier . Nama metode dan nama parameter keduanya penting. Secara umum, jika properti navigasi diberi nama "X", Anda perlu menambahkan metode bernama "GetX". Metode harus mengambil parameter bernama "kunci" yang cocok dengan jenis data kunci induk.

Penting juga untuk menyertakan atribut [FromOdataUri] dalam parameter kunci . Atribut ini memberi tahu Web API untuk menggunakan aturan sintaks OData saat mengurai kunci dari URI permintaan.

OData mendukung pembuatan atau penghapusan hubungan antara dua entitas. Dalam terminologi OData, hubungannya adalah "tautan." Setiap tautan memiliki URI dengan entitas formulir/$links/entitas. Misalnya, tautan dari produk ke pemasok terlihat seperti ini:

/Products(1)/$links/Supplier

Untuk membuat tautan baru, klien mengirim permintaan POST ke URI tautan. Isi permintaan adalah URI entitas target. Misalnya, misalkan ada pemasok dengan kunci "CTSO". Untuk membuat tautan dari "Product(1)" ke "Supplier('CTSO')", klien mengirimkan permintaan seperti berikut:

POST http://localhost/odata/Products(1)/$links/Supplier
Content-Type: application/json
Content-Length: 50

{"url":"http://localhost/odata/Suppliers('CTSO')"}

Untuk menghapus tautan, klien mengirim permintaan DELETE ke URI tautan.

Membuat Tautan

Untuk memungkinkan klien membuat tautan pemasok produk, tambahkan kode berikut ke ProductsController kelas :

[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();
    }
}

Metode ini mengambil tiga parameter:

  • kunci: Kunci ke entitas induk (produk)
  • navigationProperty: Nama properti navigasi. Dalam contoh ini, satu-satunya properti navigasi yang valid adalah "Pemasok".
  • tautan: URI OData entitas terkait. Nilai ini diambil dari isi permintaan. Misalnya, URI tautan mungkin "http://localhost/odata/Suppliers('CTSO'), yang berarti pemasok dengan ID = 'CTSO'.

Metode ini menggunakan tautan untuk mencari pemasok. Jika pemasok yang cocok ditemukan, metode mengatur Product.Supplier properti dan menyimpan hasilnya ke database.

Bagian tersulit adalah mengurai URI tautan. Pada dasarnya, Anda perlu mensimulasikan hasil pengiriman permintaan GET ke URI tersebut. Metode pembantu berikut menunjukkan cara melakukan ini. Metode ini memanggil proses perutean API Web dan mendapatkan kembali instans ODataPath yang mewakili jalur OData yang diurai. Untuk URI tautan, salah satu segmen harus menjadi kunci entitas. (Jika tidak, klien mengirim URI yang buruk.)

// 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;
}

Menghapus Tautan

Untuk menghapus tautan, tambahkan kode berikut ke ProductsController kelas :

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();

    }
}

Dalam contoh ini, properti navigasi adalah entitas tunggal Supplier . Jika properti navigasi adalah koleksi, URI untuk menghapus tautan harus menyertakan kunci untuk entitas terkait. Contohnya:

DELETE /odata/Customers(1)/$links/Orders(1)

Permintaan ini menghapus pesanan 1 dari pelanggan 1. Dalam hal ini, metode DeleteLink akan memiliki tanda tangan berikut:

void DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty);

Parameter relatedKey memberikan kunci untuk entitas terkait. Jadi dalam metode Anda DeleteLink , cari entitas utama berdasarkan parameter kunci , temukan entitas terkait dengan parameter relatedKey , lalu hapus asosiasi. Bergantung pada model data Anda, Anda mungkin perlu mengimplementasikan kedua versi DeleteLink. WEB API akan memanggil versi yang benar berdasarkan URI permintaan.