Web Api $extend IQueryable with filter

Rod*_*eek 3 c# entity-framework odata asp.net-web-api angularjs

我有一个设置,其中我获得了一个 WebApi OData 服务,该服务返回:Customers。返回客户的代码是:

public IHttpActionResult GetCustomers(ODataQueryOptions<Customer> queryOptions)
{
    return Ok(context.Customers.Where(i => i.IsActive).AsQueryable());
}
Run Code Online (Sandbox Code Playgroud)

因此,GetCustomers 方法返回所有活动客户的 IQerable 结果。出于历史目的,我们将所有客户保留在数据库中,但是当删除客户时,我们将 IsActive 字段设置为 false。

OData 设置是使用简单的 builder.EntitySet 创建的,用于为实体构建 Url。

EntitySetConfiguration<Customer> customers = builder.EntitySet<Customer>("customers");
Run Code Online (Sandbox Code Playgroud)

这完美无缺。我有一个 Angular 前端,它使用 $http 调用来接收客户等。

但是,客户可以在数据库中包含相关联系人。为了在 Angular 前端获取联系人,我使用了 OData 的 $extend 功能:

odata/customers?$expand=contacts
Run Code Online (Sandbox Code Playgroud)

这也很好用。我收到了所有相关联系人的客户。但是,正如您所猜想的那样,我只想接收应返回具有 IsActive 的联系人。IQueryable 功能将所有结果返回给我。

我知道我可以使用单独的 Odata 调用来获取联系人,但我真的很想使用 $expand 功能在一次调用中获取所有数据。我知道我也可以在客户端进行过滤(使用:$filter)。但我想在 WebApi 部分正确设置它,因此客户端不必关心过滤非活动结果。

我似乎无法弄清楚如何正确实现这一目标。有人可以帮助我走上正轨吗?

Ger*_*old 5

EntityFramework.DynamicFilters是我所知道的 Entity Framework 最伟大的工具之一。它跳入了经常请求但直到 EF6 从未实现过滤Incudes功能的差距。它依赖于 EF 的拦截 API,并在暴露一个非常简单的接口的同时完成了修改表达式的繁重工作。

在你的情况下,你可以做的是这样的:

using EntityFramework.DynamicFilters;

// In OnModelCreating (DbContext)
modelBuilder.Filter("CustomerActive", (Customers c) => c.IsActive);
Run Code Online (Sandbox Code Playgroud)

就这样!现在到处Customers查询,无论是直接查询,还是通过导航属性或在Includes 中,谓词都会添加到查询中。

你想要所有的客户吗?您可以通过执行以下操作简单地关闭每个上下文实例的过滤器

context.DisableFilter("CustomerActive");
Run Code Online (Sandbox Code Playgroud)

到目前为止,我只发现了一个小故障(或警告)。如果有两个实体,Parent并且Child有一个过滤器Parent不返回任何记录,那么这个查询......

context.Children.Include(c => c.Parent)
Run Code Online (Sandbox Code Playgroud)

...不返回任何东西。但是,从查询的形状来看,我希望它返回Child父项为空的实体。

这是因为在 SQL 中有一个INNER JOIN介于ParentandChild和 的谓词,Parent其计算结果为false。AnOUTER JOIN会给出预期的行为,但我们当然不能要求这个库变得那么聪明。