Web API 2 ile OData v3'te Varlık İlişkilerini Destekleme

Tarafından Mike Wasson

Tamamlanan Projeyi İndir

Çoğu veri kümesi varlıklar arasındaki ilişkileri tanımlar: Müşterilerin siparişleri vardır; kitapların yazarları vardır; ürünlerin tedarikçileri vardır. İstemciler, OData kullanarak varlık ilişkileri üzerinde gezinebilir. Bir ürün verdiyseniz tedarikçiyi bulabilirsiniz. ayrıca ilişkileri oluşturabilir veya kaldırabilirsiniz. Örneğin, bir ürünün tedarikçisini ayarlayabilirsiniz.

Bu öğreticide, ASP.NET Web API'sinde bu işlemlerin nasıl destekleceğiniz gösterilmektedir. Öğretici, Web API 2 ile OData v3 Uç Noktası Oluşturma öğreticisini temel alır.

Öğreticide kullanılan yazılım sürümleri

  • Web API 2
  • OData Sürüm 3
  • Entity Framework 6

Tedarikçi Varlığı Ekleme

İlk olarak OData akışımıza yeni bir varlık türü eklemeliyiz. Bir sınıf ekleyeceğiz Supplier .

using System.ComponentModel.DataAnnotations;

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

Bu sınıf, varlık anahtarı için bir dize kullanır. Uygulamada, bu bir tamsayı anahtarı kullanmaktan daha az yaygın olabilir. Ancak OData'nın tamsayılar dışında diğer anahtar türlerini nasıl işlediğine de dikkat edin.

Ardından, sınıfına bir özellik Product ekleyerek bir Supplier ilişki oluşturacağız:

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

Entity Framework'ün tabloyu veritabanına dahil edebilmesi için ProductServiceContext sınıfına Supplier yeni bir DbSet ekleyin.

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

WebApiConfig.cs dosyasında, EDM modeline bir "Suppliers" varlığı ekleyin:

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

Bir ürünün sağlayıcısını almak için istemci bir GET isteği gönderir:

GET /Products(1)/Supplier

Burada "Supplier" türündeki Product bir gezinti özelliğidir. Bu durumda, Supplier tek bir öğeye başvurur, ancak gezinti özelliği bir koleksiyon da döndürebilir (bire çok veya çoka çok ilişkisi).

Bu isteği desteklemek için sınıfına aşağıdaki yöntemi ProductsController ekleyin:

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

Anahtar parametresi, ürünün anahtarıdır. yöntemi, ilgili varlığı (bu örnekte bir Supplier örneği) döndürür. Yöntem adı ve parametre adı önemlidir. Genel olarak, gezinti özelliği "X" olarak adlandırılmışsa, "GetX" adlı bir yöntem eklemeniz gerekir. yöntemi, üst anahtarın veri türüyle eşleşen "key" adlı bir parametre almalıdır.

Anahtar parametresine [FromOdataUri] özniteliğini eklemek de önemlidir. Bu öznitelik, anahtar istek URI'sinden ayrıştırıldığında Web API'sine OData söz dizimi kurallarını kullanmasını söyler.

OData, iki varlık arasındaki ilişkilerin oluşturulmasını veya kaldırılmasını destekler. OData terminolojisinde ilişki bir "bağlantı"dır. Her bağlantı, form varlığı/$links/varlık ile bir URI'ye sahiptir. Örneğin, üründen sağlayıcıya bağlantı şöyle görünür:

/Products(1)/$links/Supplier

İstemci, yeni bir bağlantı oluşturmak için bağlantı URI'sine bir POST isteği gönderir. İsteğin gövdesi, hedef varlığın URI'sini oluşturur. Örneğin, "CTSO" anahtarına sahip bir sağlayıcı olduğunu varsayalım. "Product(1)" ile "Supplier('CTSO')" bağlantısı oluşturmak için istemci aşağıdakine benzer bir istek gönderir:

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

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

Bir bağlantıyı silmek için istemci bağlantı URI'sine bir DELETE isteği gönderir.

Bağlantı Oluşturma

İstemcinin ürün sağlayıcısı bağlantıları oluşturmasını sağlamak için sınıfına ProductsController aşağıdaki kodu ekleyin:

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

Bu yöntem üç parametre alır:

  • key: Üst varlığın anahtarı (ürün)
  • navigationProperty: Gezinti özelliğinin adı. Bu örnekte, tek geçerli gezinti özelliği "Supplier"dır.
  • bağlantı: İlgili varlığın OData URI'sini. Bu değer istek gövdesinden alınır. Örneğin, bağlantı URI'si "http://localhost/odata/Suppliers('CTSO'), kimlik = 'CTSO' olan sağlayıcı anlamına gelir.

yöntemi, sağlayıcıyı aramak için bağlantısını kullanır. Eşleşen sağlayıcı bulunursa yöntemi özelliğini ayarlar Product.Supplier ve sonucu veritabanına kaydeder.

En zor kısmı bağlantı URI'sini ayrıştırmadır. Temel olarak, bu URI'ye bir GET isteği göndermenin sonucunun benzetimini yapmanız gerekir. Aşağıdaki yardımcı yöntemi bunun nasıl yapılacağını gösterir. yöntemi, Web API yönlendirme işlemini çağırır ve ayrıştırılmış OData yolunu temsil eden bir ODataPath örneğini geri alır. Bağlantı URI'sinde segmentlerden biri varlık anahtarı olmalıdır. (Aksi takdirde, istemci hatalı bir URI gönderdi.)

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

Bağlantıları Silme

Bağlantıyı silmek için sınıfına ProductsController aşağıdaki kodu ekleyin:

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

    }
}

Bu örnekte gezinti özelliği tek Supplier bir varlıktır. Gezinti özelliği bir koleksiyonsa, bağlantı silme URI'sinin ilgili varlık için bir anahtar içermesi gerekir. Örnek:

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

Bu istek müşteri 1'den sipariş 1'i kaldırır. Bu durumda DeleteLink yöntemi aşağıdaki imzaya sahip olur:

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

relatedKey parametresi, ilgili varlığın anahtarını verir. Bu nedenle yönteminizde DeleteLinkanahtar parametresine göre birincil varlığı arayın, relatedKey parametresine göre ilgili varlığı bulun ve ilişkilendirmeyi kaldırın. Veri modelinize bağlı olarak, her iki sürümünü DeleteLinkde uygulamanız gerekebilir. Web API'sinin istek URI'sine göre doğru sürümü çağırması gerekir.