在使用 ASP.NET Web API OData v4 中打开类型Open Types in OData v4 with ASP.NET Web API

by Microsoftby Microsoft

在 OData v4打开类型是结构化的类型,其中包含动态属性,除了在类型定义中声明的任何属性。In OData v4, an open type is a structured type that contains dynamic properties, in addition to any properties that are declared in the type definition. 开放类型,可以添加到数据模型的灵活性。Open types let you add flexibility to your data models. 本教程演示如何在 ASP.NET Web API OData 中使用开放类型。This tutorial shows how to use open types in ASP.NET Web API OData.

本教程假定您已经知道如何在 ASP.NET Web API 中创建 OData 终结点。This tutorial assumes that you already know how to create an OData endpoint in ASP.NET Web API. 如果不是,应首先阅读创建 OData v4 终结点第一个。If not, start by reading Create an OData v4 Endpoint first.

在本教程中使用的软件版本Software versions used in the tutorial

  • Web API OData 5.3Web API OData 5.3
  • OData v4OData v4

首先,OData 的一些术语:First, some OData terminology:

  • 实体类型:具有一个键的结构化的类型。Entity type: A structured type with a key.
  • 复杂类型:结构化的类型没有键。Complex type: A structured type without a key.
  • 开放类型:具有动态属性的类型。Open type: A type with dynamic properties. 实体类型和复杂类型可以打开。Both entity types and complex types can be open.

动态属性的值可以是基元类型、 复杂类型或枚举类型;或任何这些类型的集合。The value of a dynamic property can be a primitive type, complex type, or enumeration type; or a collection of any of those types. 有关开放类型的详细信息,请参阅OData v4 规范For more information about open types, see the OData v4 specification.

安装 Web OData 库Install the Web OData Libraries

使用 NuGet 包管理器安装最新的 Web API OData 库。Use NuGet Package Manager to install the latest Web API OData libraries. 从包管理器控制台窗口中:From the Package Manager Console window:

Install-Package Microsoft.AspNet.OData
Install-Package Microsoft.AspNet.WebApi.OData

定义 CLR 类型Define the CLR Types

首先,定义为 CLR 类型的 EDM 模型。Start by defining the EDM models as CLR types.

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; }
}

创建实体数据模型 (EDM) 时,When the Entity Data Model (EDM) is created,

  • Category 是枚举类型。Category is an enumeration type.
  • Address 是一种复杂类型。Address is a complex type. (它没有密钥,因此它不是实体类型。)(It does not have a key, so it is not an entity type.)
  • Customer 是实体类型。Customer is an entity type. (它具有一个密钥)。(It has a key.)
  • Press 是打开的复杂类型。Press is an open complex type.
  • Book 为开放实体类型。Book is an open entity type.

若要创建开放类型,CLR 类型必须具有一个类型的属性IDictionary<string, object>,用于保存动态属性。To create an open type, the CLR type must have a property of type IDictionary<string, object>, which holds the dynamic properties.

生成的 EDM 模型Build the EDM Model

如果您使用ODataConventionModelBuilder若要创建 EDMPressBook自动添加为开放类型,根据是否存在的IDictionary<string, object>属性。If you use ODataConventionModelBuilder to create the EDM, Press and Book are automatically added as open types, based on the presence of a IDictionary<string, object> property.

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);

    }
}

您还可以构建 EDM 显式使用ODataModelBuilderYou can also build the EDM explicitly, using 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();

添加一个 OData 控制器Add an OData Controller

接下来,添加一个 OData 控制器。Next, add an OData controller. 对于本教程中,我们将使用简化的控制器仅支持获取,和 POST 请求,以及使用的内存中列表来存储实体。For this tutorial, we'll use a simplified controller that just supports GET and POST requests, and uses an in-memory list to store entities.

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", "http://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);
        }
    }
}

请注意,第一个Book实例都有任何动态属性。Notice that the first Book instance has no dynamic properties. 第二个Book实例都有以下动态属性:The second Book instance has the following dynamic properties:

  • "Published":基元类型"Published": Primitive type
  • "作者":基元类型的集合"Authors": Collection of primitive types
  • "OtherCategories":枚举类型的集合。"OtherCategories": Collection of enumeration types.

此外,Press属性的Book实例都有以下动态属性:Also, the Press property of that Book instance has the following dynamic properties:

  • "博客":基元类型"Blog": Primitive type
  • "Address":复杂类型"Address": Complex type

查询的元数据Query the Metadata

若要获取 OData 元数据文档,请将发送 GET 请求到~/$metadataTo get the OData metadata document, send a GET request to ~/$metadata. 响应正文应类似于以下所示:The response body should look similar to this:

<?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>

从元数据文档中,您可以看到:From the metadata document, you can see that:

  • 有关BookPress类型的值OpenType属性为 true。For the Book and Press types, the value of the OpenType attribute is true. CustomerAddress类型不具有此特性。The Customer and Address types don't have this attribute.
  • Book实体类型具有三个声明的属性:ISBN、 标题和按。The Book entity type has three declared properties: ISBN, Title, and Press. OData 元数据不包括Book.Properties从 CLR 类的属性。The OData metadata does not include the Book.Properties property from the CLR class.
  • 同样,Press复杂类型具有只有两个声明的属性:名称和类别。Similarly, the Press complex type has only two declared properties: Name and Category. 元数据不包括Press.DynamicProperties从 CLR 类的属性。The metadata does not include the Press.DynamicProperties property from the CLR class.

查询的实体Query an Entity

若要获取对书籍 ISBN 等于"978-0-7356-7942-9",将发送 GET 请求到~/Books('978-0-7356-7942-9')To get the book with ISBN equal to "978-0-7356-7942-9", send a GET request to ~/Books('978-0-7356-7942-9'). 响应正文应类似于以下。The response body should look similar to the following. (缩进,以使其更具可读性。)(Indented to make it more readable.)

{
  "@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":"http://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"
  ]
}

请注意,动态属性以内联形式包含声明的属性。Notice that the dynamic properties are included inline with the declared properties.

POST 实体POST an Entity

若要添加的书实体,发送 POST 请求到~/BooksTo add a Book entity, send a POST request to ~/Books. 客户端可以在请求负载中设置动态属性。The client can set dynamic properties in the request payload.

下面是示例请求。Here is an example request. 请注意"Price"和"已发布"属性。Note the "Price" and "Published" properties.

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"
}

如果在控制器方法中设置断点,可以看到 Web API 添加到这些属性Properties字典。If you set a breakpoint in the controller method, you can see that Web API added these properties to the Properties dictionary.

其他资源Additional Resources

OData 开放类型示例OData Open Type Sample