Implémenter une API OData avec ASP.NET Web API et Entity Framework Code-First

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.

Étiquettes: OData ASP.NET Web API Entity Framework Code-First CSharp

Publié le 20 juillet à 05h11