Ouvrir des types dans OData v4 avec API Web ASP.NET
par Microsoft
Dans OData v4, un type ouvert est un type structuré qui contient des propriétés dynamiques, en plus des propriétés déclarées dans la définition de type. Les types ouverts vous permettent d’ajouter de la flexibilité à vos modèles de données. Ce tutoriel montre comment utiliser des types ouverts dans API Web ASP.NET OData.
Ce tutoriel part du principe que vous savez déjà comment créer un point de terminaison OData dans API Web ASP.NET. Si ce n’est pas le cas, commencez par lire Créer un point de terminaison OData v4 .
Versions logicielles utilisées dans le tutoriel
- API web OData 5.3
- OData v4
Tout d’abord, certaines terminologies OData :
- Type d’entité : type structuré avec une clé.
- Type complexe : type structuré sans clé.
- Type ouvert : type avec des propriétés dynamiques. Les types d’entité et les types complexes peuvent être ouverts.
La valeur d’une propriété dynamique peut être un type primitif, un type complexe ou un type d’énumération ; ou une collection de l’un de ces types. Pour plus d’informations sur les types ouverts, consultez la spécification OData v4.
Installer les bibliothèques Web OData
Utilisez le Gestionnaire de package NuGet pour installer les bibliothèques OData de l’API web les plus récentes. À partir de la fenêtre Console du Gestionnaire de package :
Install-Package Microsoft.AspNet.OData
Install-Package Microsoft.AspNet.WebApi.OData
Définir les types CLR
Commencez par définir les modèles EDM en tant que types CLR.
public enum Category
{
Book,
Magazine,
EBook
}
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Press
{
public string Name { get; set; }
public string Email { get; set; }
public Category Category { get; set; }
public IDictionary<string, object> DynamicProperties { get; set; }
}
public class Book
{
[Key]
public string ISBN { get; set; }
public string Title { get; set; }
public Press Press { get; set; }
public IDictionary<string, object> Properties { get; set; }
}
Lorsque le modèle de données d’entité (EDM) est créé,
Category
est un type d’énumération.Address
est un type complexe. (Il n’a pas de clé, il ne s’agit donc pas d’un type d’entité.)Customer
est un type d’entité. (Il a une clé.)Press
est un type complexe ouvert.Book
est un type d’entité ouvert.
Pour créer un type ouvert, le type CLR doit avoir une propriété de type IDictionary<string, object>
, qui contient les propriétés dynamiques.
Générer le modèle EDM
Si vous utilisez ODataConventionModelBuilder pour créer l’EDM et Press
Book
sont automatiquement ajoutés en tant que types ouverts, en fonction de la présence d’une IDictionary<string, object>
propriété.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Book>("Books");
builder.EntitySet<Customer>("Customers");
var model = builder.GetEdmModel();
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: model);
}
}
Vous pouvez également générer explicitement l’EDM à l’aide d’ODataModelBuilder.
ODataModelBuilder builder = new ODataModelBuilder();
ComplexTypeConfiguration<Press> pressType = builder.ComplexType<Press>();
pressType.Property(c => c.Name);
// ...
pressType.HasDynamicProperties(c => c.DynamicProperties);
EntityTypeConfiguration<Book> bookType = builder.EntityType<Book>();
bookType.HasKey(c => c.ISBN);
bookType.Property(c => c.Title);
// ...
bookType.ComplexProperty(c => c.Press);
bookType.HasDynamicProperties(c => c.Properties);
// ...
builder.EntitySet<Book>("Books");
IEdmModel model = builder.GetEdmModel();
Ajouter un contrôleur OData
Ensuite, ajoutez un contrôleur OData. Pour ce tutoriel, nous allons utiliser un contrôleur simplifié qui prend uniquement en charge les requêtes GET et POST, et utilise une liste en mémoire pour stocker des entités.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData;
namespace MyApp.Controllers
{
public class BooksController : ODataController
{
private IList<Book> _books = new List<Book>
{
new Book
{
ISBN = "978-0-7356-8383-9",
Title = "SignalR Programming in Microsoft ASP.NET",
Press = new Press
{
Name = "Microsoft Press",
Category = Category.Book
}
},
new Book
{
ISBN = "978-0-7356-7942-9",
Title = "Microsoft Azure SQL Database Step by Step",
Press = new Press
{
Name = "Microsoft Press",
Category = Category.EBook,
DynamicProperties = new Dictionary<string, object>
{
{ "Blog", "https://blogs.msdn.com/b/microsoft_press/" },
{ "Address", new Address {
City = "Redmond", Street = "One Microsoft Way" }
}
}
},
Properties = new Dictionary<string, object>
{
{ "Published", new DateTimeOffset(2014, 7, 3, 0, 0, 0, 0, new TimeSpan(0))},
{ "Authors", new [] { "Leonard G. Lobel", "Eric D. Boyd" }},
{ "OtherCategories", new [] {Category.Book, Category.Magazine}}
}
}
};
[EnableQuery]
public IQueryable<Book> Get()
{
return _books.AsQueryable();
}
public IHttpActionResult Get([FromODataUri]string key)
{
Book book = _books.FirstOrDefault(e => e.ISBN == key);
if (book == null)
{
return NotFound();
}
return Ok(book);
}
public IHttpActionResult Post(Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// For this sample, we aren't enforcing unique keys.
_books.Add(book);
return Created(book);
}
}
}
Notez que la première Book
instance n’a pas de propriétés dynamiques. Le deuxième Book
instance a les propriétés dynamiques suivantes :
- « Publié » : type primitif
- « Authors » : Collection de types primitifs
- « OtherCategories » : collection de types d’énumération.
En outre, la Press
propriété de cette Book
instance a les propriétés dynamiques suivantes :
- « Blog » : type primitif
- « Adresse » : type complexe
Interroger les métadonnées
Pour obtenir le document de métadonnées OData, envoyez une requête GET à ~/$metadata
. Le corps de la réponse doit ressembler à ceci :
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="MyApp.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Book" OpenType="true">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Name="ISBN" Type="Edm.String" Nullable="false" />
<Property Name="Title" Type="Edm.String" />
<Property Name="Press" Type="MyApp.Models.Press" />
</EntityType>
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Address" Type="MyApp.Models.Address" />
</EntityType>
<ComplexType Name="Press" OpenType="true">
<Property Name="Name" Type="Edm.String" />
<Property Name="Category" Type="MyApp.Models.Category" Nullable="false" />
</ComplexType>
<ComplexType Name="Address">
<Property Name="City" Type="Edm.String" />
<Property Name="Street" Type="Edm.String" />
</ComplexType>
<EnumType Name="Category">
<Member Name="Book" Value="0" />
<Member Name="Magazine" Value="1" />
<Member Name="EBook" Value="2" />
</EnumType>
</Schema>
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="Container">
<EntitySet Name="Books" EntityType="MyApp.Models.Book" />
<EntitySet Name="Customers" EntityType="MyApp.Models.Customer" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Dans le document de métadonnées, vous pouvez voir ce qui suit :
- Pour les
Book
types etPress
, la valeur de l’attributOpenType
est true. LesCustomer
types etAddress
n’ont pas cet attribut. - Le
Book
type d’entité a trois propriétés déclarées : ISBN, Title et Press. Les métadonnées OData n’incluent pas laBook.Properties
propriété de la classe CLR. - De même, le
Press
type complexe a seulement deux propriétés déclarées : Name et Category. Les métadonnées n’incluent pas laPress.DynamicProperties
propriété de la classe CLR.
Interroger une entité
Pour obtenir le livre avec ISBN égal à « 978-0-7356-7942-9 », envoyez une requête GET à ~/Books('978-0-7356-7942-9')
. Le corps de la réponse doit ressembler à ce qui suit. (Mise en retrait pour la rendre plus lisible.)
{
"@odata.context":"http://localhost:37141/$metadata#Books/$entity",
"ISBN":"978-0-7356-7942-9",
"Title":"Microsoft Azure SQL Database Step by Step",
"Press":{
"Name":"Microsoft Press",
"Category":"EBook",
"Blog":"https://blogs.msdn.com/b/microsoft_press/",
"Address":{
"@odata.type":"#MyApp.Models.Address",
"City":"Redmond",
"Street":"One Microsoft Way"
}
},
"Published":"2014-07-03T00:00:00Z",
"Authors@odata.type":"#Collection(String)",
"Authors":[
"Leonard G. Lobel","Eric D. Boyd"
],
"OtherCategories@odata.type":"#Collection(MyApp.Models.Category)",
"OtherCategories":[
"Book","Magazine"
]
}
Notez que les propriétés dynamiques sont incluses dans les propriétés déclarées.
POST d’une entité
Pour ajouter une entité Book, envoyez une requête POST à ~/Books
. Le client peut définir des propriétés dynamiques dans la charge utile de la requête.
Voici un exemple de demande. Notez les propriétés « Price » et « Published ».
POST http://localhost:37141/Books HTTP/1.1
User-Agent: Fiddler
Host: localhost:37141
Content-Type: application/json
Content-Length: 191
{
"ISBN":"978-0-7356-8383-9","Title":"Programming Microsoft ASP.NET MVC","Press":{
"Name":"Microsoft Press","Category":"Book"
}, "Price": 49.99, "Published":"2014-02-15T00:00:00Z"
}
Si vous définissez un point d’arrêt dans la méthode du contrôleur, vous pouvez voir que l’API web a ajouté ces propriétés au Properties
dictionnaire.