Podpora vztahů mezi entitami v OData v3 pomocí webového rozhraní API 2
Mike Wasson
Většina datových sad definuje vztahy mezi entitami: Zákazníci mají objednávky; knihy mají autory; výrobky mají dodavatele. Pomocí OData můžou klienti procházet vztahy entit. Vzhledem k produktu můžete najít dodavatele. Můžete také vytvořit nebo odebrat relace. Můžete například nastavit dodavatele produktu.
V tomto kurzu se dozvíte, jak tyto operace podporovat ve webovém rozhraní API ASP.NET. Kurz vychází z kurzu Vytvoření koncového bodu OData v3 pomocí webového rozhraní API 2.
Verze softwaru použité v tomto kurzu
- Webové rozhraní API 2
- OData verze 3
- Entity Framework 6
Přidání entity dodavatele
Nejprve musíme do datového kanálu OData přidat nový typ entity. Přidáme Supplier
třídu.
using System.ComponentModel.DataAnnotations;
namespace ProductService.Models
{
public class Supplier
{
[Key]
public string Key { get; set; }
public string Name { get; set; }
}
}
Tato třída používá řetězec pro klíč entity. V praxi to může být méně běžné než použití celočíselného klíče. Stojí ale za to vidět, jak OData kromě celých čísel zpracovává i jiné typy klíčů.
Dále vytvoříme relaci přidáním Supplier
vlastnosti do Product
třídy:
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; }
}
Do třídy přidejte novou DbSetProductServiceContext
, aby Entity Framework zahrnovala Supplier
tabulku do databáze.
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; }
}
V souboru WebApiConfig.cs přidejte do modelu EDM entitu Dodavatelé:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
// New code:
builder.EntitySet<Supplier>("Suppliers");
Vlastnosti navigace
Pokud chce klient získat dodavatele produktu, odešle požadavek GET:
GET /Products(1)/Supplier
Tady "Dodavatel" je navigační vlastnost pro Product
typ. V tomto případě Supplier
odkazuje na jednu položku, ale navigační vlastnost může také vrátit kolekci (relace 1:N nebo M:N).
Pro podporu tohoto požadavku přidejte do třídy následující metodu ProductsController
:
// 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;
}
Parametr key je klíč produktu. Metoda vrátí související entitu – v tomto případě Supplier
instanci. Název metody a název parametru jsou důležité. Obecně platí, že pokud má vlastnost navigace název "X", musíte přidat metodu s názvem "GetX". Metoda musí přijmout parametr s názvem "key", který odpovídá datovému typu nadřazeného klíče.
Do klíčového parametru je také důležité zahrnout atribut [FromOdataUri]. Tento atribut říká webovému rozhraní API, aby při analýze klíče z identifikátoru URI požadavku používalo pravidla syntaxe OData.
Vytváření a odstraňování odkazů
OData podporuje vytváření nebo odebírání relací mezi dvěma entitami. V terminologii OData je relace "odkaz". Každý odkaz má identifikátor URI s entitou formuláře, $links nebo entitou. Například odkaz mezi produktem a dodavatelem vypadá takto:
/Products(1)/$links/Supplier
Pokud chcete vytvořit nový odkaz, klient odešle požadavek POST na identifikátor URI odkazu. Tělo požadavku je identifikátor URI cílové entity. Předpokládejme například, že existuje dodavatel s klíčem CTSO. Pokud chcete vytvořit odkaz z "Product(1)" na "Supplier('CTSO')", odešle klient požadavek podobný následujícímu:
POST http://localhost/odata/Products(1)/$links/Supplier
Content-Type: application/json
Content-Length: 50
{"url":"http://localhost/odata/Suppliers('CTSO')"}
Pokud chcete odstranit odkaz, klient odešle požadavek DELETE na identifikátor URI odkazu.
Vytváření odkazů
Pokud chcete klientovi umožnit vytváření odkazů na dodavatele produktů, přidejte do ProductsController
třídy následující kód:
[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();
}
}
Tato metoda přijímá tři parametry:
- key: Klíč nadřazené entity (produkt)
- navigationProperty: Název vlastnosti navigace. V tomto příkladu je jedinou platnou navigační vlastností "Dodavatel".
- link: Identifikátor URI OData související entity. Tato hodnota je převzata z textu požadavku. Například identifikátor URI odkazu může být "
http://localhost/odata/Suppliers('CTSO')
, což znamená dodavatel s ID = CTSO.
Metoda pomocí odkazu vyhledá dodavatele. Pokud je nalezen odpovídající dodavatel, metoda nastaví Product.Supplier
vlastnost a uloží výsledek do databáze.
Nejobtížnější částí je parsování identifikátoru URI odkazu. V podstatě musíte simulovat výsledek odeslání požadavku GET na tento identifikátor URI. Následující pomocná metoda ukazuje, jak to udělat. Metoda vyvolá proces směrování webového rozhraní API a získá zpět instanci ODataPath , která představuje analyzovanou cestu OData. U identifikátoru URI odkazu by jedním ze segmentů měl být klíč entity. (Pokud ne, klient odeslal chybný identifikátor URI.)
// 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;
}
Odstraňují se odkazy
Pokud chcete odstranit odkaz, přidejte do ProductsController
třídy následující kód:
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();
}
}
V tomto příkladu je navigační vlastností jedna Supplier
entita. Pokud je vlastnost navigace kolekce, musí identifikátor URI pro odstranění odkazu obsahovat klíč pro související entitu. Příklad:
DELETE /odata/Customers(1)/$links/Orders(1)
Tato žádost odebere objednávku 1 ze zákazníka 1. V tomto případě bude mít metoda DeleteLink následující podpis:
void DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty);
Parametr relatedKey poskytuje klíč pro související entitu. Takže v metodě vyhledejte DeleteLink
primární entitu podle parametru klíče , vyhledejte související entitu podle parametru relatedKey a pak odeberte přidružení. V závislosti na datovém modelu může být potřeba implementovat obě verze nástroje DeleteLink
. Webové rozhraní API bude volat správnou verzi na základě identifikátoru URI požadavku.