Un service OData expoce un modèle de données (EDM) et accepte des requêtes via des options système. Cela évite d'écrire un point d'accès spécifique pour chaque critère de recherche, le tri ou la pagination.
Configuration du routeur OData dans ASP.NET Web API
Dans le fichier WebApiConfig.cs, on déclare un modèle EDM à partir des entités du domaine. Chaque EntitySet expose une collection via un chemin OData.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Role>("Roles");
builder.EntitySet<User>("Users");
builder.EntitySet<UserRole>("UserRoles");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "odata",
model: builder.GetEdmModel());
}
}
Modèle de données avec Entity Framewrok Code-First
Le contexte définit les DbSet et hérite de DbContext. Les classses du domaine sont ici simplifiées.
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
: base("name=ODataDemo")
{
}
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
}
Contrôleur OData
Le contrôleur hérite de ODataController. Les méthodes portent les attributs EnableQuery ou gèrent les verbes HTTP. Le paramètre Delta<T> permet les mises à jour partielles.
using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.Http.OData;
using ODataDemo.Models;
namespace ODataDemo.Controllers
{
public class RolesController : ODataController
{
private readonly ApplicationDbContext _db = new ApplicationDbContext();
[EnableQuery]
public IQueryable<Role> Get()
{
return _db.Roles;
}
[EnableQuery]
public SingleResult<Role> Get([FromODataUri] int key)
{
return SingleResult.Create(_db.Roles.Where(r => r.Id == key));
}
public IHttpActionResult Post(Role item)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
_db.Roles.Add(item);
_db.SaveChanges();
return Created(item);
}
public IHttpActionResult Put([FromODataUri] int key, Delta<Role> patch)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var current = _db.Roles.Find(key);
if (current == null)
return NotFound();
patch.Put(current);
_db.SaveChanges();
return Updated(current);
}
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] int key, Delta<Role> patch)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var current = _db.Roles.Find(key);
if (current == null)
return NotFound();
patch.Patch(current);
_db.SaveChanges();
return Updated(current);
}
public IHttpActionResult Delete([FromODataUri] int key)
{
var current = _db.Roles.Find(key);
if (current == null)
return NotFound();
_db.Roles.Remove(current);
_db.SaveChanges();
return StatusCode(HttpStatusCode.NoContent);
}
protected override void Dispose(bool disposing)
{
if (disposing)
_db.Dispose();
base.Dispose(disposing);
}
}
}
Requêtes OData courantes
Les options système permettent d'interroger les entités exposées sur une collection telle que /odata/Roles.
| Option | Exemple d'URL | Description |
|---|---|---|
$filter |
?$filter=Name eq 'Admin' |
Filtrer selon une expression booléenne. |
$orderby |
?$orderby=Name desc |
Trier les résultats. |
$top |
?$top=20 |
Limite le nombre de résultats retournés. |
$skip |
?$skip=10 |
Saute un nombre donné d'éléments. |
$select |
?$select=Id,Name |
Ne retourne que les propriétés choisies. |
$expand |
?$expand=Users |
Charge une propriété de navigation liée. |
$count |
?$count=true |
Inclut le nombre total d'éléments. |