Как создать пользовательский объект для веб-API 2 OData Data controller

Мне нужно перенести мои традиционные контроллеры данных Web API 2 на контроллеры данных в стиле OData v4. Это очень легко работает со стандартными отношениями "таблица-сущность-один-к-одному", но теперь мне нужно использовать несколько разных таблиц (которые не имеют реальных ограничений) в моем ответе контроллера данных. У меня возникают проблемы с выяснением, как зарегистрировать этот новый пользовательский "объект" в моем файле WebAPI.config.

Вот пример того, как выглядит мой WebAPIconfig.cs:

using System.Linq;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using System.Web.OData.Routing;
using System.Web.OData.Routing.Conventions;
using MyProject.Models;

namespace MyProject
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and routes
            config.MapHttpAttributeRoutes();

            //OData configuration
            ODataModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Order>("orders");
            builder.EntitySet<Customer>("customers");

            //what goes here for my "custom" entity?

            var _model = builder.GetEdmModel();
            var defaultConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting(config, _model);

            //var defaultConventions = ODataRoutingConventions.CreateDefault();
            var conventions = defaultConventions.Except(
                    defaultConventions.OfType<MetadataRoutingConvention>());

            config.MapODataServiceRoute(
                routeName: "ODataRoute",
                routePrefix: "api",
                routingConventions: conventions,
                pathHandler: new DefaultODataPathHandler(),
                model: _model);         

            //ensure JSON responses
            var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
            config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
        }
    }
}

Вот пример того, как выглядит мой контроллер данных Web API 2:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using MyProject.Models;

namespace MyProject.DataControllers
{
    public class OrderDetailsController : ApiController
    {
        public OrderDetails GetOrderDetails(int id)
        {
            var ctx = new MyDatabaseEntities();
            var details = new OrderDetails();
            var order = ctx.Orders.FirstOrDefault(o => o.orderID == id);
            if (order == null)
            {
                return details; //return an empty details object to the UI and abandon this code
            }

            //Data objects necessary for the order details page
            IEnumerable<orderCertification> coupons = ctx.Coupons;
            var importances     = ctx.OrderImportances.Where(x => x.ImportanceId == order.ImportanceId).Where(x => (x.Type == "IMPORTANCES")) ?? null;
            var rankings        = ctx.OrderImportances.Where(x => x.ImportanceId == order.ImportanceId).Where(x => (x.Type == "RANK")) ?? null;
            var profits         = ctx.OrderImportances.Where(x => x.ImportanceId == order.ImportanceId).Where(x => (x.Type == "PROFIT")) ?? null;
            var address         = ctx.CustomerAddress.Where(c => c.OrderId == order.Id) ?? null;
            var email           = ctx.CustomerEmail.Where(c => c.Id == order.Id).Where(x => x.Type == "EMAIL") ?? null;         
            var giftcards       = ctx.GiftCardAssignments.Where(c => c.CardID == order.CardID).FirstOrDefault().ToList() ?? null;
            var customerCoupons = coupons.Where(c => giftCards.Any(o => o.GiftCardID == c.Id)).OrderBy(c => c.CouponName) ?? null;

            //lots of other fun and crazy properties get set here!! etc etc.

            //Set the order details properties 
            details.OrderImportances    = importances;
            details.OrderRankings       = rankings;
            details.OrderProfits        = profits;
            details.OrderAddress        = address;
            details.OrderEmail          = email;
            details.OrderGiftCards      = giftcards;
            details.OrderCoupons        = customerCoupons;
            details.OrderDescription    = "This is my order description string.";

            return details;
        }
    }
}

И вот пример того, как мой класс OrderDetails() выглядит в настоящее время:

using System.Collections.Generic;

namespace MyProject.Models
{
    public class OrderDetails
    {

        public IEnumerable<OrderImportance> OrderImportances { get; set; }
        public IEnumerable<OrderImportance> OrderRankings { get; set; }
        public IEnumerable<OrderImportance> OrderProfits { get; set; }
        public string OrderAddress { get; set; }
        public string OrderEmail { get; set; }
        public IEnumerable<OrderGiftCard> OrderGiftCards { get; set; }
        public IEnumerable<OrderCoupon> OrderCoupons { get; set; }
        public string OrderDescription { get; set; }

    }
}

Как мне сделать OData-версию этого контроллера Web API и как мне зарегистрировать мой класс OrderDetails для него в моем файле WebAPIConfig.cs?

1 ответ

Решение

OrderDetails не имеет ключевого свойства. Следовательно, это не тип сущности (именованный структурированный тип с ключом), а скорее сложный тип (структурированный тип без ключа, состоящий из набора свойств).

Поскольку сложные типы не имеют своей собственной идентичности (то есть ключа), они не могут быть представлены как наборы сущностей в службе OData. Это означает, что вы не будете настраивать OrderDetails в построителе моделей, и вы не будете создавать отдельный контроллер для OrderDetails,

Самый простой способ перенести существующий GetOrderDetails Метод OData - переопределить его как функцию OData, привязанную к orders сущность установлена. Действия и функции в OData v4 Использование ASP.NET Web API 2.2 предоставляет хорошее руководство по определению и настройке функций OData, но вот суть того, что вам нужно сделать.

Объявите функцию в WebApiConfig.Register:

builder.EntityType<Order>().Function("GetOrderDetails").Returns<OrderDetails>();

Определите функцию в контроллере для orders набор сущностей:

public class OrdersController : ODataController
{
    // Other methods for GET, POST, etc., go here.

    [HttpGet]
    public OrderDetails GetOrderDetails([FromODataUri] int key)
    {
        // Your application logic goes here.
    }
}

Наконец, вызовите функцию следующим образом:

GET http://host/api/orders(123)/Default.GetOrderDetails

Обратите внимание, что Default является пространством имен службы по умолчанию, которое обычно требуется при вызове связанной функции. Чтобы изменить это, вы можете установить builder.Namespace, или вы можете включить неквалифицированные вызовы функций с config.EnableUnqualifiedNameCall(true),

Другие вопросы по тегам