Jon*_*att 6 c# entity-framework odata asp.net-web-api
我现在正在教自己OData,但我遇到了一个我无法解决的问题.要么我误解了OData规范,要么我需要做一些事情来使它工作.
我已经建立了一个小型的书籍和作者实体模型(EF/CF).从作者到书籍的一对多关系非常简单的东西:
modelBuilder.Entity<Book>().HasRequired(b => b.Author);
modelBuilder.Entity<Author>().HasMany(a => a.Books);
Run Code Online (Sandbox Code Playgroud)
现在,在查询作者时,我希望能够扩展Books属性并过滤其(嵌套)属性.例如,如果我问"谁写了哈利波特的书",就像这样......
http://myBooksDatabase/Authors?$expand=Books&$filter=contains(Books/Name,'Harry Potter')&$select=Name
Run Code Online (Sandbox Code Playgroud)
...我收到此错误回复:
{
error: {
code: ""
message: "The query specified in the URI is not valid. The parent value for a property access of a property 'Name' is not a single value. Property access can only be applied to a single value."
innererror: {
message: "The parent value for a property access of a property 'Name' is not a single value. Property access can only be applied to a single value."
type: "Microsoft.OData.Core.ODataException"
stacktrace: " at Microsoft.OData.Core.UriParser.Parsers.EndPathBinder.BindEndPath(EndPathToken endPathToken) at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindEndPath(EndPathToken endPathToken) at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token) at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindFunctionParameter(FunctionParameterToken token) at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token) at Microsoft.OData.Core.UriParser.Parsers.FunctionCallBinder.<BindFunctionCall>b__8(FunctionParameterToken ar) at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at Microsoft.OData.Core.UriParser.Parsers.FunctionCallBinder.BindFunctionCall(FunctionCallToken functionCallToken) at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindFunctionCall(FunctionCallToken functionCallToken) at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token) at Microsoft.OData.Core.UriParser.Parsers.FilterBinder.BindFilter(QueryToken filter) at Microsoft.OData.Core.UriParser.ODataQueryOptionParser.ParseFilterImplementation(String filter, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource) at Microsoft.OData.Core.UriParser.ODataQueryOptionParser.ParseFilter() at System.Web.OData.Query.FilterQueryOption.get_FilterClause() at System.Web.OData.Query.Validators.FilterQueryValidator.Validate(FilterQueryOption filterQueryOption, ODataValidationSettings settings) at System.Web.OData.Query.FilterQueryOption.Validate(ODataValidationSettings validationSettings) at System.Web.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings) at System.Web.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings) at System.Web.OData.EnableQueryAttribute.ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions) at System.Web.OData.EnableQueryAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor) at System.Web.OData.EnableQueryAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)"
}-
}-
}
Run Code Online (Sandbox Code Playgroud)
我意识到我可以通过查询Books实体来获得...
http://myBooksDatabase/Books?$expand=Author&$filter=contains(Name,'Harry')
Run Code Online (Sandbox Code Playgroud)
...但我得到的问题来自于当我尝试引用嵌套属性时,无论我怎么做.上面的查询工作,并呈现整个作者实体,但如果我添加&$select=Author/Name我得到以下响应:
{
error: {
code: ""
message: "The query specified in the URI is not valid. Found a path with multiple navigation properties or a bad complex property path in a select clause. Please reword your query such that each level of select or expand only contains either TypeSegments or Properties."
innererror: {
message: "Found a path with multiple navigation properties or a bad complex property path in a select clause. Please reword your query such that each level of select or expand only contains either TypeSegments or Properties."
type: "Microsoft.OData.Core.ODataException"
stacktrace: " at Microsoft.OData.Core.UriParser.Visitors.SelectPropertyVisitor.ProcessTokenAsPath(NonSystemToken tokenIn) at Microsoft.OData.Core.UriParser.Visitors.SelectPropertyVisitor.Visit(NonSystemToken tokenIn) at Microsoft.OData.Core.UriParser.Syntactic.NonSystemToken.Accept(IPathSegmentTokenVisitor visitor) at Microsoft.OData.Core.UriParser.Parsers.SelectBinder.Bind(SelectToken tokenIn) at Microsoft.OData.Core.UriParser.Parsers.SelectExpandBinder.Bind(ExpandToken tokenIn) at Microsoft.OData.Core.UriParser.Parsers.SelectExpandSemanticBinder.Bind(IEdmStructuredType elementType, IEdmNavigationSource navigationSource, ExpandToken expandToken, SelectToken selectToken, ODataUriParserConfiguration configuration) at Microsoft.OData.Core.UriParser.ODataQueryOptionParser.ParseSelectAndExpandImplementation(String select, String expand, ODataUriParserConfiguration configuration, IEdmStructuredType elementType, IEdmNavigationSource navigationSource) at Microsoft.OData.Core.UriParser.ODataQueryOptionParser.ParseSelectAndExpand() at System.Web.OData.Query.Validators.SelectExpandQueryValidator.Validate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings) at System.Web.OData.Query.SelectExpandQueryOption.Validate(ODataValidationSettings validationSettings) at System.Web.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings) at System.Web.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings) at System.Web.OData.EnableQueryAttribute.ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions) at System.Web.OData.EnableQueryAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor) at System.Web.OData.EnableQueryAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)"
}-
}-
}
Run Code Online (Sandbox Code Playgroud)
这是我为作者和书籍设计的两个OData控制器:
namespace My.OData.Controllers
{
public class AuthorsController : ODataController
{
// GET /Author
[EnableQuery]
public IQueryable<Author> Get()
{
return MediaContext.Singleton.Authors;
}
// GET /Authors(<key>)
[EnableQuery]
public SingleResult<Author> Get([FromODataUri] Guid key)
{
var result = MediaContext.Singleton.Authors.Where(b => b.Id == key);
return SingleResult.Create(result);
}
// GET /Authors(<key>)/Books
[EnableQuery]
public IQueryable<Book> GetBooks([FromODataUri] Guid key)
{
return MediaContext.Singleton.Authors.Where(a => a.Id == key).SelectMany(author => author.Books);
}
}
public class BooksController : ODataController
{
// GET /Books
[EnableQuery]
public IQueryable<Book> Get()
{
return MediaContext.Singleton.Books;
}
// GET /Books(<key>)
[EnableQuery]
public SingleResult<Book> Get([FromODataUri] Guid key)
{
var result = MediaContext.Singleton.Books.Where(b => b.Id == key);
return SingleResult.Create(result);
}
// GET /Books(<key>)/Author
[EnableQuery]
public SingleResult<Author> GetAuthor([FromODataUri] Guid key)
{
return SingleResult.Create(MediaContext.Singleton.Books.Where(b => b.Id == key).Select(b => b.Author));
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以,就像我说的那样,还有什么我需要添加或配置来使相关实体中的引用属性工作吗?
乔纳斯,我希望你最终到达那里:)对于所有那些在家的投注者,乔纳斯已经确定了两个问题:
如果实体中至少有一个子项满足条件,则如何选择实体
如何扩展子实体,但只选择展开集中的特定列
答案1: 使用"任意"功能将作者过滤给那些书中包含书中名为"哈利波特"的书籍的人
http://myBooksDatabase/Authors?$filter=Books/any(b:contains(b/Name,'Harry Potter'))&$select=Name
Run Code Online (Sandbox Code Playgroud)
参考:http: //docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions- complete.html
5.1.1.10 Lambda运算符
OData定义了两个运算符,用于计算集合上的布尔表达式.两者都必须添加一个标识集合的导航路径.lambda运算符的参数是lambda变量名,后跟冒号(:)和布尔表达式,该表达式使用lambda变量名称来引用由导航路径标识的相关实体的属性.
5.1.1.10.1任何
any运算符将布尔表达式应用于集合的每个成员,如果表达式对于集合的任何成员为true,则返回true,否则返回false.如果集合不为空,则不带参数的any运算符返回true.
示例79:所有具有数量大于100的项目的订单 http:// host/service/Orders?$ filter = Items/any(d:d/Quantity gt 100)
5.1.1.10.2全部
all运算符将布尔表达式应用于集合的每个成员,如果表达式对集合的所有成员都为true,则返回true,否则返回false.示例80:所有仅包含数量大于100的项的订单
http:// host/service/Orders?$ filter = Items/all(d:d/Quantity gt 100)
答案2: 在Books的'expand'语句中使用嵌套的'$ select'来限制应该在扩展中返回的列
http://myBooksDatabase/Authors?$filter=Books/any(b:contains(b/Name,'Harry Potter'))&$select=Name&$expand=Books($select=Name,ISBN)
Run Code Online (Sandbox Code Playgroud)
也适用于提供的其他示例:
http://myBooksDatabase/Books?$expand=Author($select=Name)&$filter=contains(Name,'Harry')&$select=Name,ISBN
Run Code Online (Sandbox Code Playgroud)
但是这两个查询并不完全相同,第一个查询会找到那些用名字写"哈利波特"的书的作者,但是$ expand会列出作者所写的所有书籍,即使是'哈利波特'不在名字中.
这并不是一个完整的结果集,只是一个例子来说明这一点,请注意The Belesle the Bard的故事中不包括名字中的哈利波特字符串,但它被归还是因为作者已经写了其他书籍确实有哈利·波特之名.
[
{ Name: "J K Rowling", Books: [
{ Name: "Harry Potter and the Philosopher's Stone", ISBN: "9781408855652" },
{ Name: "The Tales of Beedle the Bard", ISBN: "9780747599876" },
{ Name: "Harry Potter and the Cursed Child - Parts I and II", ISBN: "9780751565355" }
] },
{ Name: "Bruce Foster", Books: [
{ Name: "Harry Potter: A Pop-Up Book: Based on the Film Phenomenon", ISBN: "9781608870080" }
]}
]
Run Code Online (Sandbox Code Playgroud)
第二个查询将返回数据库中的所有书籍,无论作者如何都使用名称中的"哈利波特",但将包含作者姓名:
[
{ Name: "Harry Potter and the Philosopher's Stone", ISBN: "9781408855652", Author: { Name: "J K Rowling" } },
{ Name: "Harry Potter and the Cursed Child - Parts I and II", ISBN: "9780751565355", Author: { Name: "J K Rowling" } },
{ Name: "Harry Potter: A Pop-Up Book: Based on the Film Phenomenon", ISBN: "9781608870080", Author: { Name: "Bruce Foster" } }
]
Run Code Online (Sandbox Code Playgroud)
因此,虽然OP建议您可以通过更改主控制器来选择来获取类似数据,但数据的形状可能不同,并且可能包含冗余/复制信息,或者可能会过滤掉您期望的行.如果更改控制器,您将更改数据的结果形状,因此不要在哈希中做出类似的决定.
请注意,虽然OData v4规范包含许多使用$ select和$ expand的不同选项,但并非所有这些语法选项都通过官方NuGet包提供的ASP.Net Web API实现支持...
我不确定官方推理,但到目前为止,我并没有因为规范的有限实现而处于不利地位(当涉及嵌套$ expand和$ select时)
此解决方案中提供的示例已针对Microsoft.AspNet.OData v5.6.0 - 5.9.1程序包进行了测试
| 归档时间: |
|
| 查看次数: |
4393 次 |
| 最近记录: |