开发者

Why wouldn't my oData Service (entity framework) allow navigation properties

开发者 https://www.devze.com 2023-04-11 01:30 出处:网络
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

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


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消