为什么我的 oData 服务(实体框架)不允许导航属性

发布于 2024-12-08 06:44:01 字数 290 浏览 0 评论 0原文

如果我在 Web 项目中使用我的实体模型,我可以很好地导航到 1-* 1-0.1 nav 属性...但是当我通过 oData 服务中的 LinqPad 加载完全相同的对象时,nav 属性始终为 null

...我做错了什么吗?...应该以某种方式启用它吗?

如果我加载 fiddler 并运行查询 http://odata.site.com/Service1.svc/usda_FOOD_DES(1001)/usda_ABBREV

...它会返回正确的结果

谢谢, 史蒂夫

If I use my entity model in a web project I can navigate to the 1-* 1-0.1 nav properties fine...but when I load that exact same object via LinqPad at my oData Service the nav property is always null

...am I doing something wrong?...should it be enabled somehow?

If I load up fiddler and run the query http://odata.site.com/Service1.svc/usda_FOOD_DES(1001)/usda_ABBREV

...it returns the correct result

Thanks,
Steve

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏九 2024-12-15 06:44:02

我也问自己同样的问题。我想知道为什么以下返回一个空集合:

Customers.Where(c => c.ID == 1).Single().Orders

原因是,与实体框架的导航属性不同,当您访问 OData 实体的属性时,它不会自动返回到数据源并检索尚未存在的任何数据。因此,当您从 OData 源检索实体时,默认情况下它不包含链接的实体,因此您会得到一个空的 IEnumerable(一对多或多对多导航属性)或 null(对于一对多或多对多导航属性)单个实体导航属性。

您可能希望上述查询翻译为:

http;//odata.sample.com/MyODataFeed.svc/Customers(1)/Orders

这将返回所有订单,但它会转换为以下内容(要在 LINQPad 中查看查询,请单击结果窗格上方的 SQL 按钮):

http;//odata.sample.com/MyODataFeed.svc/Customers(1)

这是因为最后的 .Orders 不在任何扩展方法内,因此不参与构造IQueryable。因此,它不会反映在根据查询构造的 URL 中,也不包含在结果集中。

那么如何才能只获取客户 1 的订单呢?您可以尝试使用以下查询来解决该问题:

Customers.Where(c => c.ID == 1).Select(a => c.Orders)

这应该会导致 .Orders 属性包含在查询中。不幸的是,不允许使用 .Select(...) 而不将结果投影到匿名方法(即 new { ... }),并且会抛出 NotSupportedException:不支持“Select”方法。 真可惜。那么怎么样:

Customers.Where(c => c.ID == 1).Select(a => new { c.Orders })

这不会产生预期的结果,而是执行以下查询:

http;//odata.sample.com/MyODataFeed.svc/Customers(1)?$expand=Orders&$select=Orders

我不明白为什么这根本没有转换为 /Customers(1)/Orders。无论如何,它将订单列表放入不需要的包装类中,这可能非常烦人,但它有效,而且它是我能检索导航属性内容的最接近的方法。

我更喜欢的方法是包含每个客户的所有订单,如下所示:

Customers.Expand("Orders").Where(c => c.ID == 1).Single().Orders

这会产生以下查询:

http;//odata.sample.com/MyODataFeed.svc/Customers(1)?$expand=订单

这可行,但缺点是包含了我们可能不需要的所有客户详细信息。此外, .Expand(...) 的字符串性是有问题的 - 它具有弱类型,因此不会被编译器验证(注意拼写错误),并非所有重构工具都可以验证它也会重构它,查找该属性的所有用法不会在搜索结果中包含该字符串等。

I asked myself the same question. I was wondering why the following returned an empty collection:

Customers.Where(c => c.ID == 1).Single().Orders

The reason is that unlike with Entity Framework's navigation properties, when you access a property of an OData entity it does not automatically go back to the data source and retrieve any data that is not already present. So when you retrieve an entity from an OData feed, by default it does not include the linked entities, so you get an empty IEnumerable<T> for one to many or many to many navigation properties or null for single entity navigation properties.

You would expected the above query to translate to:

http;//odata.sample.com/MyODataFeed.svc/Customers(1)/Orders

Which would return all orders, but instead it translates to this (to see the query in LINQPad, click the SQL button above the results pane):

http;//odata.sample.com/MyODataFeed.svc/Customers(1)

This is because the .Orders at the end is not within any extension method, and so does not participate in the construction of the IQueryable. Therefore it is not reflected in the URL that is constructed from the query and is not included in the result set.

So how would you get just the orders of customer 1? You could try and work around the issue by using the following query:

Customers.Where(c => c.ID == 1).Select(a => c.Orders)

This should cause the .Orders property to be included in the query. Unfortunately, a .Select(...) without projecting the result into an anonymous method (i.e. new { ... }) is not allowed and throws a NotSupportedException: The method 'Select' is not supported. What a shame. So what about:

Customers.Where(c => c.ID == 1).Select(a => new { c.Orders })

That doesn't produce the expected result and instead performs the following query:

http;//odata.sample.com/MyODataFeed.svc/Customers(1)?$expand=Orders&$select=Orders

I don't understand why this is simply not translated to /Customers(1)/Orders. In any case, it puts the list of orders inside an unneeded wrapper class which can be quite annoying, but it works, and it was the closest I could get to retrieving just the content of a navigation property.

The method I prefer is to include all the orders with each customer, as so:

Customers.Expand("Orders").Where(c => c.ID == 1).Single().Orders

This produces the following query:

http;//odata.sample.com/MyODataFeed.svc/Customers(1)?$expand=Orders

This works, but has the disadvantage of including all the customer details, which we may not need. Also, the string-yness of .Expand(...) is problematic - it is has weak typing so it won't be verified by the compiler (beware of spelling mistakes), not all refactoring tools will refactor it too, finding all usages of that property will not include that string in the search result, etc.

三岁铭 2024-12-15 06:44:02

最新 v4 Asp.Net oData 支持来自 http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations
使用导航属性有一个更简洁的解决方案。

支持网址:

GET /Products(1)/Supplier

引用:
这里的“供应商”是产品类型的导航属性。在本例中,Supplier 指的是单个项目,但导航属性也可以返回集合(一对多或多对多关系)。

要支持此请求,请将以下方法添加到 ProductsController 类中:

// GET /Products(1)/Supplier
public Supplier GetSupplier([FromODataUri] int key)
{
    Product product = _context.Products.FirstOrDefault(p => p.ID == key);
    if (product == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return product.Supplier;
}

key 参数是产品的键。该方法返回相关实体——在本例中是一个Supplier 实例。方法名称和参数名称都很重要。一般来说,如果导航属性名为“X”,则需要添加名为“GetX”的方法。该方法必须采用名为“key”的参数,该参数与父密钥的数据类型相匹配。

在关键参数中包含 [FromOdataUri] 属性也很重要。此属性告诉 Web API 在解析请求 URI 中的密钥时使用 OData 语法规则。

Latest v4 Asp.Net oData support from http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations
has a neater solution using Navigation Properties.

To support the URL:

GET /Products(1)/Supplier

Quote:
Here “Supplier” is a navigation property on the Product type. In this case, Supplier refers to a single item, but a navigation property can also return a collection (one-to-many or many-to-many relation).

To support this request, add the following method to the ProductsController class:

// GET /Products(1)/Supplier
public Supplier GetSupplier([FromODataUri] int key)
{
    Product product = _context.Products.FirstOrDefault(p => p.ID == key);
    if (product == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return product.Supplier;
}

The key parameter is the key of the product. The method returns the related entity—in this case, a Supplier instance. The method name and parameter name are both important. In general, if the navigation property is named “X”, you need to add a method named “GetX”. The method must take a parameter named “key” that matches the data type of the parent’s key.

It is also important to include the [FromOdataUri] attribute in the key parameter. This attribute tells Web API to use OData syntax rules when it parses the key from the request URI.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文