我有一个设置,我得到一个WebApi OData服务返回:客户。退回客户的代码是:
public IHttpActionResult GetCustomers(ODataQueryOptions<Customer> queryOptions)
{
return Ok(context.Customers.Where(i => i.IsActive).AsQueryable());
}
因此,GetCustomers方法返回所有活动客户的IQuerable结果。出于历史目的,我们将所有客户留在数据库中,但是当客户被删除时,我们将IsActive字段设置为false。
使用简单的builder.EntitySet创建OData设置,为实体构建Url。
EntitySetConfiguration<Customer> customers = builder.EntitySet<Customer>("customers");
这完美无瑕。我有一个Angular前端,它使用$ http调用来接收客户等。
但是,客户可以在数据库中包含相关联系人。要获取Angular前端中的联系人,我使用OData的$ extend功能:
odata/customers?$expand=contacts
这也很有效。我收到了所有相关联系人的客户。但是,正如您猜测的那样,我希望只接收具有IsActive的联系人。 IQueryable功能为我提供了所有结果。
我知道我可以使用单独的Odata调用来获取联系人,但我真的想使用$ expand功能在一次调用中获取所有数据。我知道我也可以在客户端进行过滤(使用:$ filter)。但是我想在WebApi部分正确设置它,因此客户端不必关心过滤非活动结果。
我似乎无法弄清楚如何正确实现这一目标。有人可以帮助我走上正轨吗?
答案 0 :(得分:4)
EntityFramework.DynamicFilters是我所知道的实体框架最好的工具之一。它跳进了经常要求的但是直到EF6从未实现过滤的Incude
s功能的空白。它依赖于EF的拦截API,并且在暴露非常简单的界面的同时完成了修改表达式的繁重工作。
在你的情况下,你可以做的是这样的:
using EntityFramework.DynamicFilters;
// In OnModelCreating (DbContext)
modelBuilder.Filter("CustomerActive", (Customers c) => c.IsActive);
这就是全部!现在,无论在哪里查询Customers
,无论是直接,通过导航属性还是在Include
中,谓词都会被添加到查询中。
您想要所有客户吗?您可以通过执行
简单地按照上下文实例关闭过滤器context.DisableFilter("CustomerActive");
到目前为止,我发现只有一个小故障(或警告)。如果有两个实体,Parent
和Child
并且Parent
上有一个不会返回任何记录的过滤器,则此查询...
context.Children.Include(c => c.Parent)
...不会返回任何内容。但是,从查询的形状来看,我希望它返回空父项的Child
个实体。
这是因为在SQL中INNER JOIN
和Parent
之间有Child
,而Parent
上的谓词评估为false
。 OUTER JOIN
会给出预期的行为,但当然我们不能要求此库 智能。
答案 1 :(得分:2)
数据模型:
public class Customer
{
public int Id { get; set; }
public bool IsActive { get; set; }
public ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id { get; set; }
public bool IsActive { get; set; }
}
带有固定数据的控制器:
public class CustomersController : ODataController
{
private List<Customer> customers = new List<Customer>
{
new Customer { Id = 1, IsActive = false },
new Customer { Id = 2, IsActive = true,
Contacts = new List<Contact>
{
new Contact { Id = 101, IsActive = true },
new Contact { Id = 102, IsActive = false },
new Contact { Id = 103, IsActive = true },
}
}
};
[EnableQuery]
public IHttpActionResult Get()
{
return Ok(customers.Where(c => c.IsActive).AsQueryable());
}
}
请注意,一个客户处于活动状态,该客户有2个(总共3个)活动联系人。
最后,配置您的OData服务:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("customers");
config.MapODataServiceRoute(
routeName: "OData",
routePrefix: null,
model: builder.GetEdmModel());
}
}
现在按如下方式调用服务:
GET http://host/customers?$expand=Contacts($filter=IsActive eq true)
您应该收到与此类似的有效载荷:
{
"@odata.context": "http:/host/$metadata#customers",
"value": [
{
"Id": 2,
"IsActive": true,
"Contacts": [
{
"Id": 101,
"IsActive": true
},
{
"Id": 103,
"IsActive": true
}
]
}
]
}
答案 2 :(得分:0)
一种可能的解决方案是添加 Views 来表示您实际想要公开的数据。
您可以拥有客户和联系 视图,它们只是原始表格的过滤版本。
回到C#方面,您的模型可以直接引用 Views ,就像它们是表格一样。
好处是它们将被视为表,所有延迟加载,导航属性和数据库端过滤仍然可以像引用原始表一样工作。