如何为 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)
.
启用非限定函数调用
我需要将我的传统 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)
.