Akce a funkce v OData v4 s využitím webového rozhraní API ASP.NET 2.2

Mike Wasson

V systému OData představují akce a funkce způsob, jak přidat chování na straně serveru, které není snadné definovat jako operace CRUD u entit. Tento kurz ukazuje, jak přidat akce a funkce do koncového bodu OData v4 pomocí webového rozhraní API 2.2. Kurz vychází z kurzu Vytvoření koncového bodu OData v4 pomocí ASP.NET webového rozhraní API 2.

Verze softwaru použité v kurzu

  • Webové rozhraní API 2.2
  • OData v4
  • Visual Studio 2013 (visual Studio 2017 si můžete stáhnout zde)
  • .NET 4.5

Verze kurzů

Informace o službě OData verze 3 najdete v tématu Akce OData v ASP.NET Webovém rozhraní API 2.

Rozdíl mezi akcemi a funkcemi spočívá v tom, že akce můžou mít vedlejší účinky, ale funkce ne. Akce i funkce můžou vracet data. Mezi použití akcí patří:

  • Složité transakce.
  • Manipulace s několika entitami najednou
  • Povolení aktualizací pouze určitých vlastností entity
  • Odesílání dat, která nejsou entitou.

Funkce jsou užitečné pro vrácení informací, které přímo neodpovídají entitě nebo kolekci.

Akce (nebo funkce) může cílit na jednu entitu nebo kolekci. V terminologii OData se jedná o vazbu. Můžete také mít "nevázané" akce nebo funkce, které se ve službě volají jako statické operace.

Příklad: Přidání akce

Pojďme definovat akci pro hodnocení produktu.

Nejprve přidejte model, který ProductRating bude představovat hodnocení.

namespace ProductService.Models
{
    public class ProductRating
    {
        public int ID { get; set; }
        public int Rating { get; set; }
        public int ProductID { get; set; }
        public virtual Product Product { get; set; }  
    }
}

Do třídy také přidejte DbSetProductsContext, aby EF vytvořil tabulku Ratings v databázi.

public class ProductsContext : DbContext
{
    public ProductsContext() 
            : base("name=ProductsContext")
    {
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    // New code:
    public DbSet<ProductRating> Ratings { get; set; }
}

Přidání akce do EDM

V souboru WebApiConfig.cs přidejte následující kód:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>()
    .Action("Rate")
    .Parameter<int>("Rating");

Metoda EntityTypeConfiguration.Action přidá akci do modelu EDM (Entity Data Model). Metoda Parameter určuje zadaný parametr pro akci.

Tento kód také nastaví obor názvů pro EDM. Na oboru názvů záleží, protože identifikátor URI akce obsahuje plně kvalifikovaný název akce:

http://localhost/Products(1)/ProductService.Rate

Poznámka

V typické konfiguraci služby IIS tečka v této adrese URL způsobí, že služba IIS vrátí chybu 404. Tento problém můžete vyřešit přidáním následující části do souboru Web.Config:

<system.webServer>
    <handlers>
      <clear/>
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/*" 
          verb="*" type="System.Web.Handlers.TransferRequestHandler" 
          preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

Přidání metody kontroleru pro akci

Akci Rate (Sazba) povolíte tak, že do ProductsControllersouboru přidáte následující metodu :

[HttpPost]
public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    int rating = (int)parameters["Rating"];
    db.Ratings.Add(new ProductRating
    {
        ProductID = key,
        Rating = rating
    });

    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateException e)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return StatusCode(HttpStatusCode.NoContent);
}

Všimněte si, že název metody odpovídá názvu akce. Atribut [HttpPost] určuje, že metoda je metoda HTTP POST.

K vyvolání akce klient odešle požadavek HTTP POST podobný tomuto:

POST http://localhost/Products(1)/ProductService.Rate HTTP/1.1
Content-Type: application/json
Content-Length: 12

{"Rating":5}

Akce Sazba je vázaná na instance produktu, takže identifikátor URI akce je plně kvalifikovaný název akce připojený k identifikátoru URI entity. (Vzpomeňte si, že jsme obor názvů EDM nastavili na ProductService, takže plně kvalifikovaný název akce je ProductService.Rate.)

Text požadavku obsahuje parametry akce jako datovou část JSON. Webové rozhraní API automaticky převede datovou část JSON na objekt ODataActionParameters , který je pouze slovníkem hodnot parametrů. Tento slovník použijte pro přístup k parametrům v metodě kontroleru.

Pokud klient odešle parametry akce v nesprávném formátu, hodnota ModelState.IsValid je false. Zkontrolujte tento příznak v metodě kontroleru a pokud isValid má hodnotu false, vraťte chybu.

if (!ModelState.IsValid)
{
    return BadRequest();
}

Příklad: Přidání funkce

Teď přidáme funkci OData, která vrací nejdražší produkt. Stejně jako předtím je prvním krokem přidání funkce do EDM. V souboru WebApiConfig.cs přidejte následující kód.

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>().Collection
    .Function("MostExpensive")
    .Returns<double>();

V tomto případě je funkce svázaná s kolekcí Products, nikoli s jednotlivými instancemi Produktu. Klienti funkci vyvolají odesláním požadavku GET:

GET http://localhost:38479/Products/ProductService.MostExpensive

Tady je metoda kontroleru pro tuto funkci:

public class ProductsController : ODataController
{
    [HttpGet]
    public IHttpActionResult MostExpensive()
    {
        var product = db.Products.Max(x => x.Price);
        return Ok(product);
    }

    // Other controller methods not shown.
}

Všimněte si, že název metody odpovídá názvu funkce. Atribut [HttpGet] určuje, že metoda je metoda HTTP GET.

Tady je odpověď HTTP:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 00:44:07 GMT
Content-Length: 85

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Decimal","value":50.00
}

Příklad: Přidání nevázané funkce

V předchozím příkladu byla funkce vázaná na kolekci. V tomto dalším příkladu vytvoříme nevázanou funkci. Nevázané funkce se ve službě volají jako statické operace. Funkce v tomto příkladu vrátí DPH pro dané PSČ.

V souboru WebApiConfig přidejte funkci do EDM:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Function("GetSalesTaxRate")
    .Returns<double>()
    .Parameter<int>("PostalCode");

Všimněte si, že funkci voláme přímo na ODataModelBuilder místo typu entity nebo kolekce. Tím sdělíte tvůrci modelů, že funkce je nevázaná.

Tady je metoda kontroleru, která implementuje funkci:

[HttpGet]
[ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
{
    double rate = 5.6;  // Use a fake number for the sample.
    return Ok(rate);
}

Nezáleží na tom, do kterého kontroleru webového rozhraní API tuto metodu umístíte. Můžete ho vložit do ProductsControllernebo definovat samostatný kontroler. Atribut [ODataRoute] definuje šablonu identifikátoru URI pro funkci.

Tady je příklad požadavku klienta:

GET http://localhost:38479/GetSalesTaxRate(PostalCode=10) HTTP/1.1

Odpověď HTTP:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 01:05:32 GMT
Content-Length: 82

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Double","value":5.6
}