Tworzenie punktu końcowego OData w wersji 4 przy użyciu internetowego interfejsu API ASP.NET

Protokół Open Data Protocol (OData) to protokół dostępu do danych dla sieci Web. OData zapewnia jednolity sposób wykonywania zapytań o zestawy danych i manipulowania nimi za pomocą operacji CRUD (tworzenie, odczytywanie, aktualizowanie i usuwanie).

ASP.NET internetowy interfejs API obsługuje protokół w wersji 3 i 4. Możesz nawet mieć punkt końcowy w wersji 4, który działa obok punktu końcowego w wersji 3.

W tym samouczku pokazano, jak utworzyć punkt końcowy OData w wersji 4, który obsługuje operacje CRUD.

Wersje oprogramowania używane w samouczku

  • Internetowy interfejs API 5.2
  • OData 4
  • Visual Studio 2017 (pobierz program Visual Studio 2017 tutaj)
  • Entity Framework 6
  • .NET 4.7.2

Wersje samouczka

Aby uzyskać informacje o danych OData w wersji 3, zobacz Creating an OData v3 Endpoint (Tworzenie punktu końcowego OData w wersji 3).

Tworzenie projektu programu Visual Studio

W programie Visual Studio z menu Plik wybierz pozycję Nowy>projekt.

Rozwiń węzeł Installed Visual C#Web (Zainstalowano program>Visual C#>Web) i wybierz szablon ASP.NET Web Application (.NET Framework). Nadaj projektowi nazwę "ProductService".

Zrzut ekranu przedstawiający okno nowego projektu programu Visual Studio z opcjami menu umożliwiającymi utworzenie aplikacji internetowej S P dot NET za pomocą programu dot NET Framework.

Wybierz przycisk OK.

Zrzut ekranu przedstawiający aplikację internetową A S P dot NET z dostępnymi szablonami umożliwiającymi utworzenie aplikacji z folderem Web A P I i podstawowym odwołaniem.

Wybierz szablon Pusty . W obszarze Dodaj foldery i podstawowe odwołania dla: wybierz pozycję Internetowy interfejs API. Wybierz przycisk OK.

Instalowanie pakietów OData

Z menu Narzędzia wybierz pozycję Konsola menedżera pakietów NuGetPackage Manager>. W oknie Konsola menedżera pakietów wpisz:

Install-Package Microsoft.AspNet.Odata

To polecenie instaluje najnowsze pakiety NuGet OData.

Dodawanie klasy modelu

Model to obiekt reprezentujący jednostkę danych w aplikacji.

W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Models. Z menu kontekstowego wybierz pozycję Dodaj>klasę.

Zrzut ekranu przedstawiający okno eksploratora rozwiązań z wyróżnioną ścieżką w celu dodania obiektu klasy modelu do projektu.

Uwaga

Zgodnie z konwencją klasy modeli są umieszczane w folderze Modele, ale nie trzeba przestrzegać tej konwencji we własnych projektach.

Nadaj klasie Productnazwę . W pliku Product.cs zastąp standardowy kod następującym kodem:

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

Właściwość Id jest kluczem jednostki. Klienci mogą wykonywać zapytania o jednostki według klucza. Na przykład aby uzyskać produkt o identyfikatorze 5, identyfikator URI to /Products(5). Właściwość Id będzie również kluczem podstawowym w bazie danych zaplecza.

Włączanie programu Entity Framework

Na potrzeby tego samouczka użyjemy programu Entity Framework (EF) Code First do utworzenia bazy danych zaplecza.

Uwaga

Dane OData internetowego interfejsu API nie wymagają platformy EF. Użyj dowolnej warstwy dostępu do danych, która może tłumaczyć jednostki bazy danych na modele.

Najpierw zainstaluj pakiet NuGet dla programu EF. Z menu Narzędzia wybierz pozycję Konsola menedżera pakietów NuGetPackage Manager>. W oknie Konsola menedżera pakietów wpisz:

Install-Package EntityFramework

Otwórz plik Web.config i dodaj następującą sekcję wewnątrz elementu konfiguracji po elemecie 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>

To ustawienie dodaje parametry połączenia dla bazy danych LocalDB. Ta baza danych będzie używana podczas lokalnego uruchamiania aplikacji.

Następnie dodaj klasę o nazwie ProductsContext do folderu Models:

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

W konstruktorze "name=ProductsContext" podaje nazwę parametrów połączenia.

Konfigurowanie punktu końcowego OData

Otwórz plik App_Start/WebApiConfig.cs. Dodaj następujące instrukcje using :

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

Następnie dodaj następujący kod do metody Register :

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

Ten kod wykonuje dwie czynności:

  • Tworzy model danych jednostki (EDM).
  • Dodaje trasę.

Model EDM jest abstrakcyjnym modelem danych. Moduł EDM służy do tworzenia dokumentu metadanych usługi. Klasa ODataConventionModelBuilder tworzy EDM przy użyciu domyślnych konwencji nazewnictwa. Takie podejście wymaga najmniejszego kodu. Jeśli chcesz mieć większą kontrolę nad modułem EDM, możesz użyć klasy ODataModelBuilder do utworzenia modułu EDM, dodając jawnie właściwości, klucze i właściwości nawigacji.

Trasa informuje internetowy interfejs API, jak kierować żądania HTTP do punktu końcowego. Aby utworzyć trasę OData w wersji 4, wywołaj metodę rozszerzenia MapODataServiceRoute .

Jeśli aplikacja ma wiele punktów końcowych OData, utwórz oddzielną trasę dla każdego z nich. Nadaj każdej trasie unikatową nazwę i prefiks trasy.

Dodawanie kontrolera OData

Kontroler jest klasą, która obsługuje żądania HTTP. Należy utworzyć oddzielny kontroler dla każdego zestawu jednostek w usłudze OData. W tym samouczku utworzysz jeden kontroler dla Product jednostki.

W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers i wybierz polecenie Dodaj>klasę. Nadaj klasie ProductsControllernazwę .

Uwaga

Wersja tego samouczka dla usługi OData w wersji 3 używa szkieletu Dodaj kontroler . Obecnie nie ma szkieletu dla protokołu OData w wersji 4.

Zastąp standardowy kod w pliku ProductsController.cs następującym kodem.

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 używa klasy do uzyskiwania ProductsContext dostępu do bazy danych przy użyciu programu EF. Zwróć uwagę, że kontroler zastępuje metodę Dispose w celu usunięcia obiektu ProductsContext.

Jest to punkt wyjścia dla kontrolera. Następnie dodamy metody dla wszystkich operacji CRUD.

Wykonywanie zapytań względem zestawu jednostek

Dodaj następujące metody do ProductsControllermetody .

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

Wersja bez parametrów Get metody zwraca całą kolekcję Products. Metoda Get z parametrem klucza wyszukuje produkt według jego klucza (w tym przypadku Id właściwość ).

Atrybut [EnableQuery] umożliwia klientom modyfikowanie zapytania przy użyciu opcji zapytania, takich jak $filter, $sort i $page. Aby uzyskać więcej informacji, zobacz Obsługa opcji zapytania OData.

Dodawanie jednostki do zestawu jednostek

Aby umożliwić klientom dodawanie nowego produktu do bazy danych, dodaj następującą metodę do ProductsControllermetody .

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

Aktualizowanie jednostki

Protokół OData obsługuje dwie różne semantyki do aktualizowania jednostki, PATCH i PUT.

  • Patch wykonuje częściową aktualizację. Klient określa tylko właściwości do zaktualizowania.
  • FUNKCJA PUT zastępuje całą jednostkę.

Wadą funkcji PUT jest to, że klient musi wysyłać wartości dla wszystkich właściwości w jednostce, w tym wartości, które nie zmieniają się. Specyfikacja OData wskazuje, że opcja PATCH jest preferowana.

W każdym przypadku oto kod dla metod 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);
}

W przypadku poprawki kontroler używa typu delta<T> do śledzenia zmian.

Usuwanie jednostki

Aby umożliwić klientom usunięcie produktu z bazy danych, dodaj następującą metodę do ProductsControllermetody .

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