Vytvoření koncového bodu OData v4 pomocí webového rozhraní API ASP.NET

Open Data Protocol (OData) je protokol pro přístup k datům pro web. OData poskytuje jednotný způsob dotazování a manipulace s datovými sadami prostřednictvím operací CRUD (vytvoření, čtení, aktualizace a odstranění).

ASP.NET webové rozhraní API podporuje protokol v3 i v4. Můžete dokonce mít koncový bod v4, který běží souběžně s koncovým bodem v3.

V tomto kurzu se dozvíte, jak vytvořit koncový bod OData v4, který podporuje operace CRUD.

Verze softwaru použité v kurzu

  • Webové rozhraní API 5.2
  • OData v4
  • Visual Studio 2017 (sadu Visual Studio 2017 si můžete stáhnout zde)
  • Entity Framework 6
  • .NET 4.7.2

Verze kurzů

Informace o OData verze 3 najdete v tématu Vytvoření koncového bodu OData v3.

Vytvoření projektu sady Visual Studio

V sadě Visual Studio v nabídce Soubor vyberte Nový>projekt.

Rozbalte nainstalovaný>Visual C#>Web a vyberte šablonu ASP.NET Web Application (.NET Framework). Pojmenujte projekt ProductService.

Snímek obrazovky s oknem nového projektu sady Visual Studio s možnostmi nabídky pro vytvoření webové aplikace A S P dot NET s rozhraním NET

Vyberte OK.

Snímek obrazovky s webovou aplikací A S P dot NET zobrazující dostupné šablony pro vytvoření aplikace se složkou Web A P I a základními referenčními informacemi

Vyberte prázdnou šablonu. V části Přidat složky a základní odkazy pro: vyberte Webové rozhraní API. Vyberte OK.

Instalace balíčků OData

V nabídce Nástroje vyberteKonzola Správce>balíčků NuGet. V okně Konzola Správce balíčků zadejte:

Install-Package Microsoft.AspNet.Odata

Tento příkaz nainstaluje nejnovější balíčky NuGet OData.

Přidání třídy modelu

Model je objekt, který představuje datovou entitu ve vaší aplikaci.

V Průzkumník řešení klikněte pravým tlačítkem na složku Modely. V místní nabídce vyberte Přidat>třídu.

Snímek obrazovky s oknem průzkumníka řešení se zvýrazněnou cestou pro přidání objektu třídy modelu do projektu

Poznámka

Podle konvence se třídy modelů umisťují do složky Modely, ale tuto konvenci nemusíte dodržovat ve svých vlastních projektech.

Pojmenujte třídu Product. V souboru Product.cs nahraďte často používaný kód následujícím kódem:

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

Vlastnost Id je klíč entity. Klienti se můžou dotazovat na entity podle klíče. Pokud například chcete získat produkt s ID 5, identifikátor URI je /Products(5). Vlastnost Id bude také primárním klíčem v back-end databázi.

Povolení Entity Frameworku

V tomto kurzu použijeme Entity Framework (EF) Code First k vytvoření back-end databáze.

Poznámka

OData webového rozhraní API nevyžaduje EF. Použijte jakoukoli vrstvu přístupu k datům, která dokáže převést databázové entity na modely.

Nejprve nainstalujte balíček NuGet pro EF. V nabídce Nástroje vyberteKonzola Správce>balíčků NuGet. V okně Konzola Správce balíčků zadejte:

Install-Package EntityFramework

Otevřete soubor Web.config a přidejte následující část do elementu configuration za element configSections .

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

Toto nastavení přidá připojovací řetězec pro databázi LocalDB. Tato databáze se použije při místním spuštění aplikace.

Dále přidejte třídu s názvem ProductsContext do složky Models:

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

V konstruktoru "name=ProductsContext" zadejte název připojovacího řetězce.

Konfigurace koncového bodu OData

Otevřete soubor App_Start/WebApiConfig.cs. Přidejte následující příkazy using :

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

Pak do metody Register přidejte následující kód:

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

Tento kód dělá dvě věci:

  • Vytvoří model EDM (Entity Data Model).
  • Přidá trasu.

EDM je abstraktní model dat. EDM slouží k vytvoření dokumentu metadat služby. ODataConventionModelBuilder Třída vytvoří EDM pomocí výchozích zásad vytváření názvů. Tento přístup vyžaduje nejmenší kód. Pokud chcete mít větší kontrolu nad EDM, můžete použít třídu ODataModelBuilder k vytvoření EDM explicitním přidáním vlastností, klíčů a vlastností navigace.

Trasa říká webovému rozhraní API, jak směrovat požadavky HTTP do koncového bodu. Pokud chcete vytvořit trasu OData v4, zavolejte metodu rozšíření MapODataServiceRoute .

Pokud má vaše aplikace více koncových bodů OData, vytvořte pro každý z nich samostatnou trasu. Dejte každé trase jedinečný název a předponu trasy.

Přidání kontroleru OData

Kontroler je třída, která zpracovává požadavky HTTP. Pro každou sadu entit ve službě OData vytvoříte samostatný kontroler. V tomto kurzu vytvoříte jeden kontroler pro entitu Product .

V Průzkumník řešení klikněte pravým tlačítkem na složku Kontrolery a vyberte Přidat>třídu. Pojmenujte třídu ProductsController.

Poznámka

Verze tohoto kurzu pro OData v3 používá generování uživatelského rozhraní Přidat kontroler . V současné době není k dispozici žádné generování uživatelského rozhraní pro OData v4.

Nahraďte často používaný kód v souboru ProductsController.cs následujícím kódem.

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

Kontroler používá ProductsContext třídu pro přístup k databázi pomocí EF. Všimněte si, že kontroler přepíše Dispose metodu pro odstranění ProductsContext.

Toto je výchozí bod kontroleru. Dále přidáme metody pro všechny operace CRUD.

Dotaz na sadu entit

Přidejte následující metody do ProductsController.

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

Bezparametrová Get verze metody vrátí celou kolekci Products. Metoda Get s parametrem klíče vyhledá produkt podle jeho klíče (v tomto případě Id vlastnosti ).

Atribut [EnableQuery] umožňuje klientům upravovat dotaz pomocí možností dotazu, jako jsou $filter, $sort a $page. Další informace najdete v tématu Podpora možností dotazů OData.

Přidání entity do sady entit

Pokud chcete klientům povolit přidání nového produktu do databáze, přidejte do ProductsControllernástroje následující metodu .

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

Aktualizace entity

OData podporuje dvě různé sémantiky pro aktualizaci entity, PATCH a PUT.

  • Patch provede částečnou aktualizaci. Klient určuje pouze vlastnosti, které se mají aktualizovat.
  • PUT nahradí celou entitu.

Nevýhodou PUT je, že klient musí odeslat hodnoty pro všechny vlastnosti v entitě, včetně hodnot, které se nemění. Specifikace OData uvádí, že se upřednostňuje patch.

V každém případě je zde kód pro metody PATCH i PUT:

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

V případě PATCH používá kontroler ke sledování změn typ Delta<T> .

Odstranění entity

Pokud chcete klientům umožnit odstranění produktu z databáze, přidejte následující metodu do ProductsController.

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