ASP.NET Web API'sini Kullanarak OData v4 Uç Noktası Oluşturma

Açık Veri Protokolü (OData), web için bir veri erişim protokolüdür. OData, CRUD işlemleri (oluşturma, okuma, güncelleştirme ve silme) aracılığıyla veri kümelerini sorgulamak ve işlemek için tekdüzen bir yol sağlar.

ASP.NET Web API'si protokolün hem v3 hem de v4 sürümünü destekler. V3 uç noktasıyla yan yana çalışan bir v4 uç noktanız bile olabilir.

Bu öğreticide CRUD işlemlerini destekleyen bir OData v4 uç noktasının nasıl oluşturulacağı gösterilmektedir.

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

  • Web API 5.2
  • OData v4
  • Visual Studio 2017 (Visual Studio 2017'i buradan indirin)
  • Entity Framework 6
  • .NET 4.7.2

Öğretici sürümleri

OData Sürüm 3 için bkz. OData v3 Uç Noktası Oluşturma.

Visual Studio Projesi Oluşturma

Visual Studio'da, Dosyamenüsünden Yeni Proje'yi> seçin.

Yüklü>Visual C#>Web'i genişletin ve ASP.NET Web Uygulaması (.NET Framework) şablonunu seçin. Projeyi "ProductService" olarak adlandırın.

NoktaLı NET Framework ile A S P nokta NET Web Uygulaması oluşturmaya yönelik menü seçeneklerini gösteren Visual Studio yeni proje penceresinin ekran görüntüsü.

Tamam’ı seçin.

Web A P I klasörü ve çekirdek başvurusu ile uygulamayı oluşturmak için kullanılabilir şablonları gösteren A S P noktaLı NET Web Uygulaması'nın ekran görüntüsü.

Boş şablonu seçin. Klasör ve çekirdek başvuruları ekle:'nin altında Web API'sini seçin. Tamam’ı seçin.

OData paketlerini yükleme

Araçlar menüsünde NuGet Paket Yöneticisi Paket Yöneticisi>Konsolu'nu seçin. Paket Yöneticisi Konsolu penceresinde şunu yazın:

Install-Package Microsoft.AspNet.Odata

Bu komut en son OData NuGet paketlerini yükler.

Model sınıfı ekleme

Model, uygulamanızdaki bir veri varlığını temsil eden bir nesnedir.

Çözüm Gezgini'da Modeller klasörüne sağ tıklayın. Bağlam menüsünde Sınıf Ekle'yi>seçin.

Projeye model sınıfı nesnesi ekleme yolunu vurgulayan çözüm gezgini penceresinin ekran görüntüsü.

Not

Kural gereği, model sınıfları Modeller klasörüne yerleştirilir, ancak kendi projelerinizde bu kuralı izlemeniz gerekmez.

sınıfını Productolarak adlandırın. Product.cs dosyasında ortak kodu aşağıdakilerle değiştirin:

namespace ProductService.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }
}

Id özelliği, varlık anahtarıdır. İstemciler varlıkları anahtara göre sorgulayabilir. Örneğin, 5 kimliğine sahip ürünü almak için URI olur /Products(5). Id özelliği, arka uç veritabanındaki birincil anahtar da olacaktır.

Entity Framework'i etkinleştirme

Bu öğreticide, arka uç veritabanını oluşturmak için Entity Framework (EF) Code First kullanacağız.

Not

Web API OData ef gerektirmez. Veritabanı varlıklarını modellere çevirebilen herhangi bir veri erişim katmanını kullanın.

İlk olarak EF için NuGet paketini yükleyin. Araçlar menüsünde NuGet Paket Yöneticisi Paket Yöneticisi>Konsolu'nu seçin. Paket Yöneticisi Konsolu penceresinde şunu yazın:

Install-Package EntityFramework

Web.config dosyasını açın ve configSections öğesinin ardından yapılandırma öğesinin içine aşağıdaki bölümü ekleyin.

<configuration>
  <configSections>
    <!-- ... -->
  </configSections>

  <!-- Add this: -->
  <connectionStrings>
    <add name="ProductsContext" connectionString="Data Source=(localdb)\mssqllocaldb; 
        Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True; 
        AttachDbFilename=|DataDirectory|ProductsContext.mdf"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

Bu ayar, LocalDB veritabanı için bir bağlantı dizesi ekler. Uygulamayı yerel olarak çalıştırdığınızda bu veritabanı kullanılır.

Ardından Models klasörüne adlı ProductsContext bir sınıf ekleyin:

using System.Data.Entity;
namespace ProductService.Models
{
    public class ProductsContext : DbContext
    {
        public ProductsContext() 
                : base("name=ProductsContext")
        {
        }
        public DbSet<Product> Products { get; set; }
    }
}

Oluşturucuda bağlantı "name=ProductsContext" dizesinin adını verir.

OData uç noktasını yapılandırma

App_Start/WebApiConfig.cs dosyasını açın. Aşağıdaki using deyimlerini ekleyin:

using ProductService.Models;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;

Ardından Register yöntemine aşağıdaki kodu ekleyin:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // New code:
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<Product>("Products");
        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: builder.GetEdmModel());
    }
}

Bu kod iki işlem yapar:

  • Varlık Veri Modeli (EDM) oluşturur.
  • Bir yol ekler.

EDM, verilerin soyut bir modelidir. EDM, hizmet meta verileri belgesini oluşturmak için kullanılır. ODataConventionModelBuilder sınıfı, varsayılan adlandırma kurallarını kullanarak bir EDM oluşturur. Bu yaklaşım için en az kod gerekir. EDM üzerinde daha fazla denetime sahip olmak istiyorsanız, özellik, anahtar ve gezinti özelliklerini açıkça ekleyerek EDM'yi oluşturmak için ODataModelBuilder sınıfını kullanabilirsiniz.

Yol, Web API'sine HTTP isteklerini uç noktaya nasıl yönlendireceklerini bildirir. OData v4 yolu oluşturmak için MapODataServiceRoute uzantısı yöntemini çağırın.

Uygulamanızın birden çok OData uç noktası varsa, her biri için ayrı bir yol oluşturun. Her yola benzersiz bir yol adı ve ön eki verin.

OData denetleyicisini ekleme

Denetleyici, HTTP isteklerini işleyen bir sınıftır. OData hizmetinizdeki her varlık kümesi için ayrı bir denetleyici oluşturursunuz. Bu öğreticide varlık için Product bir denetleyici oluşturacaksınız.

Çözüm Gezgini'da Denetleyiciler klasörüne sağ tıklayın veSınıfEkle'yi> seçin. sınıfını ProductsControllerolarak adlandırın.

Not

OData v3 için bu öğreticinin sürümünde Denetleyici Ekleme iskelesi kullanılır. Şu anda OData v4 için yapı iskelesi yoktur.

ProductsController.cs dosyasındaki ortak kodu aşağıdakiyle değiştirin.

using ProductService.Models;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;
namespace ProductService.Controllers
{
    public class ProductsController : ODataController
    {
        ProductsContext db = new ProductsContext();
        private bool ProductExists(int key)
        {
            return db.Products.Any(p => p.Id == key);
        } 
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

Denetleyici, EF kullanarak veritabanına erişmek için sınıfını kullanır ProductsContext . Denetleyicinin ProductsContext'i atmak için Dispose yöntemini geçersiz kıldığını fark edin.

Bu, denetleyicinin başlangıç noktasıdır. Ardından, tüm CRUD işlemleri için yöntemler ekleyeceğiz.

Varlık kümesini sorgulama

aşağıdaki yöntemleri öğesine ProductsControllerekleyin.

[EnableQuery]
public IQueryable<Product> Get()
{
    return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
    IQueryable<Product> result = db.Products.Where(p => p.Id == key);
    return SingleResult.Create(result);
}

Yöntemin Get parametresiz sürümü Products koleksiyonunun tamamını döndürür. GetAnahtar parametresine sahip yöntemi, bir ürünü anahtarına göre arar (bu örnekte Id özelliği).

[EnableQuery] özniteliği istemcilerin $filter, $sort ve $page gibi sorgu seçeneklerini kullanarak sorguyu değiştirmesini sağlar. Daha fazla bilgi için bkz . OData Sorgu Seçeneklerini Destekleme.

Varlık kümesine varlık ekleme

İstemcilerin veritabanına yeni bir ürün eklemesini sağlamak için aşağıdaki yöntemi öğesine ProductsControllerekleyin.

public async Task<IHttpActionResult> Post(Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Created(product);
}

Varlığı güncelleştirme

OData, PATCH ve PUT varlıklarını güncelleştirmek için iki farklı semantiği destekler.

  • PATCH kısmi bir güncelleştirme gerçekleştirir. İstemci yalnızca güncelleştirilecek özellikleri belirtir.
  • PUT tüm varlığın yerini alır.

PUT'nin dezavantajı, istemcinin değişmeyen değerler de dahil olmak üzere varlıktaki tüm özellikler için değerler göndermesi gerektiğidir. OData belirtimi PATCH'in tercih edilir olduğunu belirtir.

Her durumda, hem PATCH hem de PUT yöntemlerinin kodu aşağıdadır:

public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    var entity = await db.Products.FindAsync(key);
    if (entity == null)
    {
        return NotFound();
    }
    product.Patch(entity);
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(entity);
}
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    if (key != update.Id)
    {
        return BadRequest();
    }
    db.Entry(update).State = EntityState.Modified;
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(update);
}

PATCH söz konusu olduğunda denetleyici değişiklikleri izlemek için Delta<T> türünü kullanır.

Bir varlığı silme

İstemcilerin veritabanından bir ürünü silmesini sağlamak için aşağıdaki yöntemi öğesine ProductsControllerekleyin.

public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
    var product = await db.Products.FindAsync(key);
    if (product == null)
    {
        return NotFound();
    }
    db.Products.Remove(product);
    await db.SaveChangesAsync();
    return StatusCode(HttpStatusCode.NoContent);
}