如何在 Web-Api 控制器中过滤 odata $expand 查询
How to filter an odata $expand query in a Web-Api controller
我有两个模型。 A 产品型号:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Categories> Categories { get; set; }
}
还有一个类别模型:
public class Categories
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsPublic { get; set; }
public virtual Product Products { get; set; }
}
如您所见,模型之间存在关联。一个产品可以有多个类别。我可以使用 http://localhost/Products?$expand=Categories
查询模型。
但是我想在返回类别之前对其进行过滤。主要目标是避免 api 消费者可以查询 IsPublic
设置为 false
的类别。我怎样才能在 Web API 控制器中存档它?
我的第一次尝试没有成功,因为我需要一个 IQueryable<Product>
而它 returns 一个 IQueryable<Categories>
:
[HttpGet]
[EnableQuery]
public IQueryable<Product> Get(ODataQueryOptions<Product> odataQueryOptions)
{
if (odataQueryOptions.SelectExpand.RawExpand == "Categories")
{
var p = db.Products.First();
var result = db.Entry(p).Collection(x => x.Categories).Query().Where(x => x.IsPublic == true).AsQueryable();
return result;
}
else
{
return db.Products;
}
}
基本上我的问题是:在 LINQ 中编写类似 OData 的 $expand 查询的正确方法是什么?如果这不可能,我还能如何过滤扩展导航 属性?
EF 不允许过滤包含的属性。 (这是一个高度投票 feature request for EF but never implemented: Allow filtering for Include extension method)
因此,EF 不支持它。所以我只能给你这两个技巧:
- 在数据库中创建过滤视图,并将其映射到 EF 中的不同实体。这很有效,因为过滤将在服务器中进行。
- 在控制器的代码中,将查询投影到新的 class,过滤相关集合(通过使用
Select(x=> new ...)
),并使此投影结果 IQueryable
为 .AsQueryable
扩展方法。通过这种方式,您将返回一个新的可查询对象,其中包含根据需要过滤的相关实体。这个比较低效:需要从DB server中恢复整个相关的collection,在controller的方法中过滤,转换成queryable
显然第一个选项是"best hack"。我认为不幸的是没有更好的解决方案。也许还有一些其他黑客攻击,在服务器上使用 TVF 或类似的东西。
我有两个模型。 A 产品型号:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Categories> Categories { get; set; }
}
还有一个类别模型:
public class Categories
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsPublic { get; set; }
public virtual Product Products { get; set; }
}
如您所见,模型之间存在关联。一个产品可以有多个类别。我可以使用 http://localhost/Products?$expand=Categories
查询模型。
但是我想在返回类别之前对其进行过滤。主要目标是避免 api 消费者可以查询 IsPublic
设置为 false
的类别。我怎样才能在 Web API 控制器中存档它?
我的第一次尝试没有成功,因为我需要一个 IQueryable<Product>
而它 returns 一个 IQueryable<Categories>
:
[HttpGet]
[EnableQuery]
public IQueryable<Product> Get(ODataQueryOptions<Product> odataQueryOptions)
{
if (odataQueryOptions.SelectExpand.RawExpand == "Categories")
{
var p = db.Products.First();
var result = db.Entry(p).Collection(x => x.Categories).Query().Where(x => x.IsPublic == true).AsQueryable();
return result;
}
else
{
return db.Products;
}
}
基本上我的问题是:在 LINQ 中编写类似 OData 的 $expand 查询的正确方法是什么?如果这不可能,我还能如何过滤扩展导航 属性?
EF 不允许过滤包含的属性。 (这是一个高度投票 feature request for EF but never implemented: Allow filtering for Include extension method)
因此,EF 不支持它。所以我只能给你这两个技巧:
- 在数据库中创建过滤视图,并将其映射到 EF 中的不同实体。这很有效,因为过滤将在服务器中进行。
- 在控制器的代码中,将查询投影到新的 class,过滤相关集合(通过使用
Select(x=> new ...)
),并使此投影结果IQueryable
为.AsQueryable
扩展方法。通过这种方式,您将返回一个新的可查询对象,其中包含根据需要过滤的相关实体。这个比较低效:需要从DB server中恢复整个相关的collection,在controller的方法中过滤,转换成queryable
显然第一个选项是"best hack"。我认为不幸的是没有更好的解决方案。也许还有一些其他黑客攻击,在服务器上使用 TVF 或类似的东西。