Поделиться через


Создание конечной точки OData версии 4 с помощью веб-API ASP.NET

Open Data Protocol (OData) — это протокол доступа к данным в Интернете. OData предоставляет универсальный способ запроса наборов данных и управления ими с помощью операций CRUD (создание, чтение, обновление и удаление).

веб-API ASP.NET поддерживает протокол версии 3 и 4. Вы даже можете создать конечную точку версии 4, которая работает параллельно с конечной точкой версии 3.

В этом руководстве показано, как создать конечную точку OData версии 4, которая поддерживает операции CRUD.

Версии программного обеспечения, используемые в этом руководстве

  • Веб-API 5.2
  • OData v4
  • Visual Studio 2017 (скачайте Visual Studio 2017 здесь)
  • Entity Framework 6
  • .NET 4.7.2

Версии учебников

Сведения о OData версии 3 см. в статье Создание конечной точки OData версии 3.

Создание проекта Visual Studio

В Visual Studio в меню Файл выберите Создать>проект.

Разверните узел Установленный>Visual C#>Web и выберите шаблон веб-приложение ASP.NET (платформа .NET Framework). Присвойте проекту имя ProductService.

Снимок экрана: окно нового проекта Visual Studio с параметрами меню для создания веб-приложения AP-net с точкой NET Framework.

Щелкните ОК.

Снимок экрана: веб-приложение A P P DOT NET, показывающее доступные шаблоны для создания приложения с папкой Web A P I и основной ссылкой.

Выберите шаблон Пустой. В разделе Добавление папок и основных ссылок для выберитеВеб-API. Щелкните ОК.

Установка пакетов OData

В меню Инструменты выберите Диспетчер пакетов NuGet>Консоль диспетчера пакетов. В окне Консоль диспетчера пакетов введите:

Install-Package Microsoft.AspNet.Odata

Эта команда устанавливает последние пакеты OData NuGet.

Добавление класса модели

Модель — это объект, представляющий сущность данных в приложении.

В обозревателе решений щелкните правой кнопкой мыши папку Models. В контекстном меню выберите Добавить>класс.

Снимок экрана: окно обозревателя решений с выделенным путем добавления объекта класса модели в проект.

Примечание

По соглашению классы моделей помещаются в папку Models, но вам не нужно следовать этому соглашению в собственных проектах.

Назовите класс Product. В файле Product.cs замените стандартный код следующим кодом:

namespace ProductService.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }
}

Свойство Id является ключом сущности. Клиенты могут запрашивать сущности по ключу. Например, чтобы получить продукт с идентификатором 5, универсальный код ресурса (URI) — /Products(5). Свойство Id также будет первичным ключом во внутренней базе данных.

Включение Entity Framework

В этом руководстве мы будем использовать Entity Framework (EF) Code First для создания серверной базы данных.

Примечание

Для веб-API OData не требуется EF. Используйте любой уровень доступа к данным, который может преобразовать сущности базы данных в модели.

Сначала установите пакет NuGet для EF. В меню Инструменты выберите Диспетчер пакетов NuGet>Консоль диспетчера пакетов. В окне Консоль диспетчера пакетов введите:

Install-Package EntityFramework

Откройте файл Web.config и добавьте следующий раздел в элемент конфигурации после элемента configSections .

<configuration>
  <configSections>
    <!-- ... -->
  </configSections>

  <!-- Add this: -->
  <connectionStrings>
    <add name="ProductsContext" connectionString="Data Source=(localdb)\mssqllocaldb; 
        Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True; 
        AttachDbFilename=|DataDirectory|ProductsContext.mdf"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

Этот параметр добавляет строку подключения для базы данных LocalDB. Эта база данных будет использоваться при локальном запуске приложения.

Затем добавьте класс с именем ProductsContext в папку Models:

using System.Data.Entity;
namespace ProductService.Models
{
    public class ProductsContext : DbContext
    {
        public ProductsContext() 
                : base("name=ProductsContext")
        {
        }
        public DbSet<Product> Products { get; set; }
    }
}

В конструкторе "name=ProductsContext" задает имя строки подключения.

Настройка конечной точки OData

Откройте файл App_Start/WebApiConfig.cs. Добавьте следующие операторы using :

using ProductService.Models;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;

Затем добавьте следующий код в метод Register :

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // New code:
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<Product>("Products");
        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: builder.GetEdmModel());
    }
}

Этот код выполняет две задачи:

  • Создает модель EDM.
  • Добавляет маршрут.

EDM — это абстрактная модель данных. EDM используется для создания документа метаданных службы. Класс ODataConventionModelBuilder создает EDM с помощью соглашений об именовании по умолчанию. Для этого подхода требуется минимальный код. Если требуется более полный контроль над EDM, можно использовать класс ODataModelBuilder для создания EDM путем явного добавления свойств, ключей и свойств навигации.

Маршрут сообщает веб-API, как направлять HTTP-запросы к конечной точке. Чтобы создать маршрут OData версии 4, вызовите метод расширения MapODataServiceRoute .

Если приложение имеет несколько конечных точек OData, создайте отдельный маршрут для каждой из них. Присвойте каждому маршруту уникальное имя маршрута и префикс.

Добавление контроллера OData

Контроллер — это класс, обрабатывающий HTTP-запросы. Вы создаете отдельный контроллер для каждого набора сущностей в службе OData. В этом руководстве вы создадите один контроллер для сущности Product .

В Обозреватель решений щелкните правой кнопкой мыши папку Контроллеры и выберите Добавить>класс. Назовите класс ProductsController.

Примечание

В версии этого руководства для OData версии 3 используется шаблон добавления контроллера . В настоящее время формирование шаблонов для OData версии 4 отсутствует.

Замените стандартный код в Файле ProductsController.cs следующим кодом.

using ProductService.Models;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;
namespace ProductService.Controllers
{
    public class ProductsController : ODataController
    {
        ProductsContext db = new ProductsContext();
        private bool ProductExists(int key)
        {
            return db.Products.Any(p => p.Id == key);
        } 
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

Контроллер использует класс для ProductsContext доступа к базе данных с помощью EF. Обратите внимание, что контроллер переопределяет метод Dispose для удаления ProductsContext.

Это отправная точка для контроллера. Далее мы добавим методы для всех операций CRUD.

Запрос набора сущностей

Добавьте следующие методы в ProductsController.

[EnableQuery]
public IQueryable<Product> Get()
{
    return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
    IQueryable<Product> result = db.Products.Where(p => p.Id == key);
    return SingleResult.Create(result);
}

Версия Get метода без параметров возвращает всю коллекцию Products. Метод Get с параметром ключа ищет продукт по его ключу (в данном случае Id это свойство ).

Атрибут [EnableQuery] позволяет клиентам изменять запрос с помощью таких параметров запроса, как $filter, $sort и $page. Дополнительные сведения см. в разделе Поддержка параметров запроса OData.

Добавление сущности в набор сущностей

Чтобы клиенты могли добавлять новый продукт в базу данных, добавьте следующий метод в ProductsController.

public async Task<IHttpActionResult> Post(Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Created(product);
}

Обновление сущности

OData поддерживает две различные семантики для обновления сущности: PATCH и PUT.

  • PATCH выполняет частичное обновление. Клиент указывает только свойства для обновления.
  • PUT заменяет всю сущность.

Недостаток PUT заключается в том, что клиент должен отправлять значения для всех свойств в сущности, включая значения, которые не изменяются. Спецификация OData указывает, что предпочтительно использовать PATCH.

В любом случае ниже приведен код для методов PATCH и PUT:

public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    var entity = await db.Products.FindAsync(key);
    if (entity == null)
    {
        return NotFound();
    }
    product.Patch(entity);
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(entity);
}
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    if (key != update.Id)
    {
        return BadRequest();
    }
    db.Entry(update).State = EntityState.Modified;
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(update);
}

В случае PATCH контроллер использует тип Delta<T> для отслеживания изменений.

Удаление сущности

Чтобы клиенты могли удалять продукт из базы данных, добавьте следующий метод в ProductsController.

public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
    var product = await db.Products.FindAsync(key);
    if (product == null)
    {
        return NotFound();
    }
    db.Products.Remove(product);
    await db.SaveChangesAsync();
    return StatusCode(HttpStatusCode.NoContent);
}