如何将 ODataQueryOptions<T> 应用于 IQueryable<X>

Posted

技术标签:

【中文标题】如何将 ODataQueryOptions<T> 应用于 IQueryable<X>【英文标题】:How to apply ODataQueryOptions<T> to IQueryable<X> 【发布时间】:2016-10-11 04:07:49 【问题描述】:

我希望将 ODataQueryOptions 应用于 DbSet。让我快速介绍一下我的应用程序。我需要创建一个从两个表 T1 和 T2 读取数据的应用程序。除了每个表之外,这两个表都具有几乎相同的架构,除了几列。我的实体类设计有点像下面:(为简洁起见,我保持架构和命名简单)

class Base

    [Key]
    public int ID;
    public string Name;
    public string Gender;
    public string Comment;


[Table("T1")]
class TableOneEntity : Base




[Table("T2")]
class TableTwoEntity : Base

    // extra columns from T2
    public string Country;
    public string City;

DbContext 类:

class ApplicationDbContext : DbContext

    public DbSet<TableOneEntity> TableOneDbSet;
    public DbSet<TableTwoEntity> TableTwoDbSet;

我的 OData GET API 如下所示:

public PageResult<TableTwoEntity> GetTableResult(ODataQueryOptions<TableOneEntity> options)

    var result1 = options.ApplyTo(appDbCtx.TableOneDbSet) as IQueryable<TableOneEntity>;

    var result2 = options.ApplyTo(appDbCtx.TableTwoDbSet) as IQueryable<TableTwoEntity>;

    return new PageResult<TableTwoEntity>(output, odataProperties.NextLink, odataProperties.TotalCount);

调用 GET API 时出现以下异常:

无法将“TableOneEntity”的 ODataQueryOptions 应用于 IQueryable 'TableTwoEntity'。

如何将相同的“选项”应用于 TableTwoDbSet ?

【问题讨论】:

【参考方案1】:

管道会将 URL 转换为精确到一个 EDM 实体类的转换。所以简短的回答是你不能按照你正在做的事情做你想做的事情。但是,您可以做的是创建一个复合类(如果您使用的是 EF,则使用视图):

class Base

    [Key]
    public int ID;
    public string Name;
    public string Gender;
    public string Comment;


[Table("T1")]
class TableOneEntity : Base




[Table("T2")]
class TableTwoEntity : Base

    // extra columns from T2
    public string Country;
    public string City;


[Table("V1")] // which is a view containing columns from Base, T1 and T2.
class CompositeEntity : Base

    // extra columns from T1
    .....

    // extra columns from T2
    public string Country;
    public string City;

那么你可以这样做:

[EnableQuery(PageSize = 100)] // For server-driven paging -- recommended but not obligatory
public IHttpActionResult GetTableResult()

    return appDbCtx.Composite;

请记住将您的复合类添加到 EDM(通常在 WebApiConfig.cs 中)。就是这样。 OData 管道将为您完成所有其余工作。

编辑:

事实上,你可以通过使用开放类型来做你想做的事情。见http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/use-open-types-in-odata-v4。但是,我怀疑复合类/视图在您的情况下会是更简单的选择。

【讨论】:

【参考方案2】:

我通常会引用OData WebApi nuget package。

然后,对于控制器,添加 Queryable 属性并将您的集合返回为 Queryable 就完成了,只需将 OData 查询选项添加到您的调用中。

很酷的部分是,如果您使用的是 EF,那么所有过滤器和约束都会一直传递到 DB 查询。

public class MyController : ApiController

    // Add the queryable attribute
    [Queryable]
    IQueryable<stuff> Get() 
        ...
        ...
        return myStuff.AsQueryable();

你可以像这样使用它:

http://www.blabla.bla/api/v1/stuff?$top=10&$skip=20&$orderby=name

--- 给你第 20 页,共 10 行,按名称排序。

请参阅OData conventions 以获得良好的参考:

【讨论】:

我的 ODataQueryOptions 也有分页.....会提供 Queryable 属性支持分页结果吗? 是的,它会的。只要您使用的是 OData 4。 使用分页示例更新答案 但是如果我只返回 IQueryable,客户如何知道下一个链接和总数。为此,我使用了 PageResult。 在传入的查询中包含 $count=true,管道将返回该元数据以及您的结果。

以上是关于如何将 ODataQueryOptions<T> 应用于 IQueryable<X>的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET Web API 控制器的 nunit 测试中实例化新的 System.Web.Http.OData.Query.ODataQueryOptions

WebAPI OData v4 查询不是异步的?

如何为自托管的Web API添加身份验证?

如何获取 OData 可查询 Web API 端点过滤器并从 DTO 对象映射它?

如何使用 C# 为 OData 查询中指定的每个过滤器获取一组键/值对?

OData V4 在服务器端修改 $filter