OData v3 'de Web API 2 ile varlık Ilişkilerini destekleme

, Mike te son

Tamamlanmış projeyi indir

Çoğu veri kümesi varlıklar arasındaki ilişkileri tanımlar: müşterilerin siparişleri vardır; Kitaplar yazar. ürünlerin tedarikçileri vardır. OData kullanarak, istemciler varlık ilişkilerine gidebilir. Bir ürün verildiğinde, tedarikçiyi bulabilirsiniz. Ayrıca ilişkiler oluşturabilir veya kaldırabilirsiniz. Örneğin, tedarikçiyi bir ürün için ayarlayabilirsiniz.

Bu öğreticide, ASP.NET Web API 'sinde bu işlemleri nasıl destekleyeceği gösterilmektedir. Öğretici, Web API 2 Ile OData v3 uç noktası oluşturmaöğreticisinde oluşturulur.

Öğ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ışınıza yeni bir varlık türü eklememiz gerekiyor. Supplier sınıfı ekleyeceğiz.

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, bir tamsayı anahtar kullanmaktan daha az ortak olabilir. Ancak, OData 'in diğer anahtar türlerini tamsayıların yanı sıra nasıl işleyeceğini de öğrenecektir.

Sonra, Product sınıfına bir Supplier özelliği ekleyerek bir 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; }
}

ProductServiceContext sınıfına yeni bir Dbset ekleyin, böylece Entity Framework Supplier tablosunu veritabanına dahil eder.

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 ' de, EDM modeline bir "Suppliers" varlığı ekleyin:

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

Bir ürünün tedarikçilerini almak için istemci bir GET isteği gönderir:

GET /Products(1)/Supplier

Burada "Tedarikçi" Product türünde bir gezinti özelliğidir. Bu durumda Supplier tek bir öğe anlamına gelir, ancak bir gezinti özelliği de bir koleksiyon (bire çok veya çoktan çoğa ilişki) döndürebilir.

Bu isteği desteklemek için, ProductsController sınıfına aşağıdaki yöntemi 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 bu durumda bir Supplier örneği—olan ilgili varlığı döndürür. Yöntem adı ve parametre adı her ikisi de önemlidir. Genel olarak, gezinti özelliği "X" olarak adlandırılmışsa, "GetX" adlı bir yöntem eklemeniz gerekir. Yöntem, üst öğenin anahtarını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, Web API 'sini istek URI 'sinden anahtar ayrıştırdığında OData sözdizimi kurallarını kullanmasını söyler.

OData iki varlık arasında ilişki oluşturmayı veya kaldırmayı destekler. OData terminolojisinde ilişki bir "bağlantıdır". Her bağlantının varlık/$Links/varlıkbiçiminde bir URI 'si vardır. Örneğin, üründen tedarikçiye bağlantı şu şekilde görünür:

/Products(1)/$links/Supplier

Yeni bir bağlantı oluşturmak için, istemci bağlantı URI 'sine bir POST isteği gönderir. İsteğin gövdesi, hedef varlığın URI 'sidir. Örneğin, "CTSO" anahtarına sahip bir tedarikçi olduğunu varsayalım. "Product (1)" öğesinden "tedarikçisine (' CTSO ')" bir bağlantı 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

Bir istemcinin ürün-tedarikçi bağlantıları oluşturmasını sağlamak için, ProductsController sınıfına 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:

  • anahtar: ana varlığa yönelik anahtar (ürün)
  • NavigationProperty: Gezinti özelliğinin adı. Bu örnekte, geçerli olan tek gezinti özelliği "Tedarikçi" dir.
  • bağlantı: Ilgili varlığın OData URI 'si. Bu değer, istek gövdesinden alınır. Örneğin, bağlantı URI 'SI "http://localhost/odata/Suppliers('CTSO'), yani, ID = ' CTSO ' olan tedarikçide olabilir.

Yöntemi, tedarikçiyi aramak için bağlantıyı kullanır. Eşleşen Tedarikçi bulunursa, yöntemi Product.Supplier özelliğini ayarlar ve sonucu veritabanına kaydeder.

En zor bölümü, bağlantı URI 'sini ayrıştırır. Temel olarak, bu URI 'ye bir GET isteği gönderme 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. Bir bağlantı URI 'SI için segmentlerin biri varlık anahtarı olmalıdır. (Değilse, 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 siliniyor

Bir bağlantıyı silmek için ProductsController sınıfına 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 bir Supplier varlıktır. Gezinti özelliği bir koleksiyon ise, bir bağlantıyı silmenin URI 'SI ilgili varlık için bir anahtar içermelidir. Örneğin:

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 olacaktır:

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

RelatedKey parametresi ilgili varlık için anahtarı verir. Bu nedenle DeleteLink yönteminde, anahtar parametresine göre birincil varlığı bulun, RelatedKey parametresiyle ilgili varlığı bulun ve ardından ilişkilendirmeyi kaldırın. Veri modelinize bağlı olarak her iki DeleteLinksürümünü de uygulamanız gerekebilir. Web API 'SI, istek URI 'sine bağlı olarak doğru sürümü çağırır.