如何为 Web API 2 OData 数据控制器创建自定义实体

How to create custom entity for Web API 2 OData Data controller

我需要将我的传统 Web API 2 数据控制器迁移到 OData v4 样式数据控制器。这很容易与标准的一对一 table 实体关系一起工作,但我现在需要在我的数据控制器响应中使用几个不同的 tables(没有真正的约束) .我无法弄清楚如何在我的 WebAPI.config 文件中注册这个新的自定义 "entity"。

这是我的 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() class 当前的示例:

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

    }
}

如何制作此 Web API 控制器的 OData 版本以及如何在我的 WebAPIConfig.cs 中为其注册我的 OrderDetails class?

OrderDetails 似乎没有密钥 属性。因此,它不是实体类型(带键的命名结构类型),而是复杂类型(无键命名结构类型,由一组属性)。

由于复杂类型没有自己的标识(即键),因此它们不能作为实体集在 OData 服务中公开。这意味着您不会在模型构建器中配置 OrderDetails,也不会为 OrderDetails.

创建单独的控制器

将现有 GetOrderDetails 方法迁移到 OData 的最直接方法是将其重新定义为 OData function bound to the orders entity set. Actions and Functions in OData v4 Using 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).

启用非限定函数调用