首页 > 解决方案 > 如何使用 OData 为 EF Core 无密钥实体(数据库视图)公开 Querable API?

问题描述

我正在使用https://www.nuget.org/packages/Microsoft.AspNetCore.OData/7.3.0使用 ASP.NET Core 3.1 API 公开基于 OData 4 的 Querable API

我有带有无键实体(数据库视图)的 EF 数据上下文,当我尝试使用基于 OData 约定的模型公开它时

   modelBuilder
   .Entity<Student>(eb =>
    {
        eb.HasNoKey();
        eb.ToView("vw_students", "public");
    });

这是我的带有 EF Core 实体类型的 OData EDM 模型映射

var edmBuilder = new ODataConventionModelBuilder();
edmBuilder.EntitySet<Student>("Students");       
return edmBuilder.GetEdmModel();

我在这行 edmBuilder.GetEdmModel() 收到错误实体集“Students”基于未定义键的类型“ODataCore31.Student”。

我的问题- 1) OData 模型是否原生支持无密钥实体类型?2)有什么解决方法吗?

标签: restodataasp.net-core-3.1ef-core-3.1

解决方案


要通过 OData 公开无键视图或自定义查询,您必须使用未绑定函数而不是完整的 OData 控制器,后者通过EntitySet<T>()常规构建器中的表达式进行配置。

这是有道理的,因为 EntitySet控制器允许基于项目的操作,但没有特定的,就没有传统的方法可以将特定的项目作为自己的资源来处理。

因此,无键类型是 aComplexType而不是Entity,根据定义,实体 必须有一个键,以便 OData 可以支持基于项键的导航。

将无密钥集合公开为未绑定函数的 OData 方法涉及 3 个重要步骤:

1. 在控制器中声明您的查询,以便它返回一个IQueryable<T>

Unbound Function 或 Action的一个特殊之处在于它们没有专门绑定到任何控制器,因此您可以在您选择的任何控制器中声明您的未绑定端点。

我更喜欢创建一个特定的控制器来托管所有未绑定的组件,只要它不与域中的其他控制器或实体类型冲突,名称并不重要。

与默认集合端点一样,我们需要确保我们的端点返回一个IQueryable<Student>,如果您需要该级别的抽象,您仍然可以使用IActionResult响应类型,但实现应该是可查询的。

2. 使用 设置方法的具体路线ODataAttributeRouting

这是一个至关重要的元素,在通常的基于约定的实现中,我们不会在每个方法上声明特定的路由,每个控制器的路由前缀是从控制器名称解析的,端点是从方法名称解析的。

它有助于尽可能地遵循约定以保持您的实现透明和可配置,如果您总是声明特定的路由,那么您配置的路由与注册相匹配是很重要的ODataConventionModelBuilder

未绑定端点的特定情况下,路由中根本没有控制器元素,这有两个含义:您不能使用在控制器级别指定的路由前缀,因为在 an 中将前缀留空ODataRouteComponent将触发默认逻辑,该逻辑将使用控制器类的名称;并且您必须将端点本身上的路由声明为完整路由,因为所有基于约定的路由都依赖于路由中的控制器元素以映射到控制器以解析可能的端点,因此运行时将不知道要检查哪个控制器.

您可以编写自己的路由解析逻辑,但这超出了此问题的范围。

这是一个最小的示例控制器实现:
注意:在此示例中, routePrefix是注册到 OData 服务"odata"的吗

[ODataAttributeRouting]
public partial class ViewsController : ODataController
{
    protected AppDbContext _db;
    public ViewsController(AppDbContext context)
    {
        _db = context;
    }

    [HttpGet("odata/" + nameof(Students))]
    [EnableQuery]
    public IQueryable<Student> Students(ODataQueryOptions<Student> _queryOptions)
    {
        return this._db.Students;
    }
}

3. 注册/映射路线ODataConventionModelBuilder

这一步经常被忽略,如果你跳过这一步,可以通过 HTTP 查询端点,但这是一个标准的Web API实现,而不是传统的 OData 实现,所以端点不会记录在 CSDL ( $metadata ) 或一些大摇大摆的实现,因此将不可用于 OData 客户端生成器。

var edmBuilder = new ODataConventionModelBuilder();
edmBuilder.Function("Students").ReturnsCollection<Student>();     
return edmBuilder.GetEdmModel();

内联服务注册看起来像这样,注意方法中声明的"odata"routePrefix AddRouteComponents,它必须匹配自定义路由声明前缀。

services.AddControllers().AddOData(opt =>
{
    var edmBuilder = new ODataConventionModelBuilder();
    edmBuilder.Function("Students").ReturnsCollection<Student>();   

    opt.AddRouteComponents("odata", edmBuilder.GetEdmModel()).EnableQueryFeatures();
}

ASP.NET Core 3.1该解决方案是使用 OData 8.0(用于 .Net 5.0)测试和编写的,这些概念与 OP 的请求和 OData Lib v7.3完全兼容,但是某些命名空间是不同的。7.3 可以轻松升级到 8 或 9,我鼓励您这样做,因为最新版本中有显着的性能和功能改进。


推荐阅读