{"id":3537,"date":"2019-12-30T14:16:36","date_gmt":"2019-12-30T21:16:36","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/odata\/?p=3537"},"modified":"2019-12-31T15:46:15","modified_gmt":"2019-12-31T22:46:15","slug":"select-enhancement-in-asp-net-core-odata","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/select-enhancement-in-asp-net-core-odata\/","title":{"rendered":"$select Enhancement in ASP.NET Core OData"},"content":{"rendered":"<p>The release of <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNetCore.OData\/7.3.0\">ASP.NET Core OData v7.3<\/a> brings a ton of improvements to <strong>$select<\/strong> functionality. In this article, I\u2019d like to introduce some of the new features of <strong>$select<\/strong> and its usages in combination with other query options like <strong>$filter<\/strong>, <strong>$top<\/strong>, <strong>$skip<\/strong>, <strong>$orderby<\/strong>, <strong>$count<\/strong> and <strong>$expand<\/strong>.\nThis tutorial assumes that you already have the knowledge to build an ASP.NET Core Web Application service using ASP.NET Core OData NuGget package. If not, start by reading <a href=\"https:\/\/devblogs.microsoft.com\/odata\/asp-net-core-odata-now-available\/\">ASP.NET Core OData now Available<\/a> and refer to the <a href=\"https:\/\/github.com\/xuzhg\/MyAspNetCore\/tree\/master\/src\/SelectImprovement\">sample project<\/a> used in this article.\nLet\u2019s get started.<\/p>\n<h4>Data Model<\/h4>\n<p>As mentioned, we are going to skip the steps to create an ASP.NET Core Web Application with OData functionalities enabled. However, to get a good understanding of the scenarios listed in this article, it is important for us to see the model types used in this sample project.<\/p>\n<p>Below are the CLR class types used in sample project:<\/p>\n<pre class=\"lang:c# decode:true \">\/\/ Entity type\r\npublic class Customer\r\n{\r\n    public int Id { get; set; }\r\n    public string Name { get; set; }\r\n    public IList&lt;string&gt; Emails { get; set; }\r\n    public Address HomeAddress { get; set; }\r\n    public IList&lt;Address&gt; FavoriteAddresses { get; set; }\r\n    public Order PersonOrder { get; set; }\r\n    public Order[] Orders { get; set; }\r\n}\r\n\r\n\/\/ Complex type\r\npublic class Address\r\n{\r\n    public string Street { get; set; }\r\n    public string City { get; set; }\r\n    public ZipCode ZipCode { get; set; }\r\n}\r\n\r\n\/\/ Complex type\r\npublic class BillAddress : Address\r\n{\r\n    public string FirstName { get; set; }\r\n    public string LastName { get; set; }\r\n}\r\n\r\n\/\/ Entity type\r\npublic class Order\r\n{\r\n    public int Id { get; set; }\r\n    public string Title { get; set; }\r\n}\r\n\r\n\/\/ Entity Type\r\npublic class ZipCode\r\n{\r\n    public int Id { get; set; }\r\n    public string DisplayName { get; set; }\r\n}\r\n<\/pre>\n<p>Where,<\/p>\n<ul>\n<li>Type \u201c<strong>Customer<\/strong>\u201d, \u201c<strong>Order<\/strong>\u201d, \u201c<strong>ZipCode<\/strong>\u201d serve as Edm entity types.<\/li>\n<li>Type \u201c<strong>Address<\/strong>\u201d and \u201c<strong>BillAddress<\/strong>\u201d serve as Edm complex types, and \u201c<strong>BillAddress<\/strong>\u201d is derived from \u201c<strong>Address<\/strong>\u201d.<\/li>\n<li>\u201c<strong>Address<\/strong>\u201d has a navigation property named \u201c<strong>ZipCode<\/strong>&#8220;.<\/li>\n<\/ul>\n<p>In the corresponding Edm model, I have \u201c<strong>Customers<\/strong>\u201d, \u201c<strong>Orders<\/strong>\u201d and \u201c<strong>ZipCodes<\/strong>\u201d as the Edm entity sets related to the above Edm types.<\/p>\n<p>Besides, I have two <em>real<\/em> \u201cCustomers\u201d named \u201cBalmy\u201d and \u201cChilly\u201d in the sample project. For other properties\u2019 information, you can refer the source code or build, run, and send a <a href=\"http:\/\/localhost:5000\/odata\/Customers\">http:\/\/localhost:5000\/odata\/Customers<\/a> to get.<\/p>\n<h4>$select<\/h4>\n<p><strong>$select<\/strong> is one of OData supported query options which allows the clients to select specific properties from the server. The biggest advantage of using <strong>$select<\/strong> is that the heavy lifting is done by the server before the data is returned, which leads to better performance. Hassan Habib&#8217;s <a href=\"https:\/\/devblogs.microsoft.com\/odata\/optimizing-web-applications-with-odata-select\/\" rel=\"bookmark\">Optimizing Web Applications with OData $Select<\/a> shares some advantages to use <strong>$select<\/strong>.<\/p>\n<p>For example, we can select a complex property using <strong>$select<\/strong> like:<\/p>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(1)?$select=HomeAddress<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress)\/$entity\",\r\n    \"HomeAddress\": {\r\n        \"Street\": \"145TH AVE\",\r\n        \"City\": \"Redonse\"\r\n    }\r\n}\r\n<\/pre>\n<p>In this way, we can get data performance by limiting the result only including the properties wanted. That is, server doesn\u2019t need to return all properties belong to \u201cCustomer(1)\u201d, meanwhile the client doesn\u2019t need to trim the result.<\/p>\n<h4>Select path in $select<\/h4>\n<p>The above example is a very basic usage of <strong>$select<\/strong>. With the release of <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNetCore.OData\/7.3.0\">ASP.NET Core OData v7.3.0<\/a>, we now have support to use <em>select path<\/em> in <strong>$select<\/strong>.<\/p>\n<p>For example:<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(1)?$select=HomeAddress\/Street<\/pre>\n<p><span style=\"font-size: 1rem;\">In summary, the select path should follow up the following basic rules:<\/span><\/p>\n<ul>\n<li>If only one segment exists, it could be \u201c<strong>*<\/strong>\u201d, \u201c<strong>NS.*<\/strong>\u201d, \u201c<strong>Structural property<\/strong>&#8221; segment or \u201c<strong>Navigation property<\/strong>\u201d segment.<\/li>\n<li>Otherwise, the last segment in a select path could be \u201c<strong>Structural property<\/strong>\u201d segment or \u201c<strong>Navigation property<\/strong>\u201d segment, and the other segments could be \u201c<strong>Complex property<\/strong>\u201d segment or \u201c<strong>Complex type cast<\/strong>\u201d segment.<\/li>\n<\/ul>\n<p>For the above request, we can get the following result:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress\/Street)\/$entity\",\r\n    \"HomeAddress\": {\r\n        \"Street\": \"145TH AVE\"\r\n    }\r\n}<\/pre>\n<p><strong>Note<\/strong>: The result only includes the <strong>Street <\/strong>in <strong>HomeAddress<\/strong>.<\/p>\n<h4>Type cast select path in $select<\/h4>\n<p>It also supports the type cast in the select path, for example:<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers?$select=HomeAddress\/SelectImprovement.Models.BillAddress\/FirstName<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress\/SelectImprovement.Models.BillAddress\/FirstName)\",\r\n    \"value\": [\r\n        {\r\n            \"HomeAddress\": {}\r\n        },\r\n        {\r\n            \"HomeAddress\": {\r\n                \"@odata.type\": \"#SelectImprovement.Models.BillAddress\",\r\n                \"FirstName\": \"Peter\"\r\n            }\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p><strong>Note:<\/strong><\/p>\n<ul>\n<li>The first customer\u2019s <strong>HomeAddress <\/strong>is not a <strong>BillAddress<\/strong>, so the entity for this customer only includes the <strong>HomeAddress<\/strong> property with empty object.<\/li>\n<li>The second customer\u2019s <strong>HomeAddress<\/strong> is a <strong>BillAddress<\/strong>, so it includes the selected property named <strong>FirstName<\/strong> and a control metadata property named <strong>@odata.type<\/strong>.<\/li>\n<\/ul>\n<h4>Nested $select in $select<\/h4>\n<p>We can use the nested <strong>$select<\/strong> to replace the above select path. In fact, It&#8217;s more understandable to use nested <strong>$select<\/strong>.<\/p>\n<p>A simplified example should look as follows:<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers?$select=HomeAddress($select=Street)<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress)\",\r\n    \"value\": [\r\n        {\r\n            \"HomeAddress\": {\r\n                \"Street\": \"145TH AVE\"\r\n            }\r\n        },\r\n        {\r\n            \"HomeAddress\": {\r\n                \"@odata.type\": \"#SelectImprovement.Models.BillAddress\",\r\n                \"Street\": \"Main ST\"\r\n            }\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p><strong>Note<\/strong>, the context URI in this scenario is not correct. It should be same as the context URI in select path scenario. It\u2019s a known issue and will be fixed in the future release.<\/p>\n<h4>Select sub navigation property in $select<\/h4>\n<p>It also supports to select the sub navigation property in a complex property. <span style=\"font-size: 1rem;\">For example:<\/span><\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers?$select=HomeAddress\/ZipCode<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress\/ZipCode)\",\r\n    \"value\": [\r\n        {\r\n            \"HomeAddress\": {}\r\n        },\r\n        {\r\n            \"HomeAddress\": {\r\n                \"@odata.type\": \"#SelectImprovement.Models.BillAddress\"\r\n            }\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p>You may be wondering &#8220;Why the <strong>HomeAddress<\/strong> is empty object here?&#8221; It\u2019s empty not because it&#8217;s not a <strong>BillAddress<\/strong>, but because the navigation link control information is omitted by default in the \u201c<strong>Minimal<\/strong>\u201d metadata level. In OData, if we don\u2019t set the metadata level, by default it&#8217;s \u201c<strong>Minimal<\/strong>\u201d metadata level. So, if we want to get all control metadata information, we can use <strong>$format<\/strong> to set the &#8220;<strong>Full<\/strong>&#8221; metadata level as below:<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(1)?$select=HomeAddress\/ZipCode&amp;$format=application\/json;odata.metadata=full<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress\/ZipCode)\/$entity\",\r\n    \"@odata.type\": \"#SelectImprovement.Models.Customer\",\r\n    \"@odata.id\": \"http:\/\/localhost:5000\/odata\/Customers(1)\",\r\n    \"@odata.editLink\": \"http:\/\/localhost:5000\/odata\/Customers(1)\",\r\n    \"HomeAddress\": {\r\n        \"@odata.type\": \"#SelectImprovement.Models.Address\",\r\n        \"ZipCode@odata.associationLink\": \"http:\/\/localhost:5000\/odata\/Customers(1)\/HomeAddress\/ZipCode\/$ref\",\r\n        \"ZipCode@odata.navigationLink\": \"http:\/\/localhost:5000\/odata\/Customers(1)\/HomeAddress\/ZipCode\"\r\n    }\r\n}\r\n<\/pre>\n<p>Again, we can use nested <strong>$select<\/strong> to get the same payload result as below (except the context URI):<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(1)?$select=HomeAddress($select=ZipCode)&amp;$format=application\/json;odata.metadata=full<\/pre>\n<h4>Selection on collection property<\/h4>\n<p>Now, there&#8217;s support to select path and nested select on collection complex property. For example:<\/p>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(2)?$select=FavoriteAddresses\/Street\r\nor\r\nhttp:\/\/localhost:5000\/odata\/Customers(2)?$select=FavoriteAddresses($select=Street)<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(FavoriteAddresses\/Street)\/$entity\",\r\n    \"FavoriteAddresses\": [\r\n        {\r\n            \"Street\": \"145TH AVE\"\r\n        },\r\n        {\r\n            \"@odata.type\": \"#SelectImprovement.Models.BillAddress\",\r\n            \"Street\": \"Main ST\"\r\n        },\r\n        {\r\n            \"Street\": \"32ST NE\"\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p><strong>Note<\/strong>: The result in select path and nested select is almost same except the context URI.<\/p>\n<h4>Nested $filter, $top, $skip, $orderby, $count<\/h4>\n<p>Besides the nested <strong>$select<\/strong>, there is support for nested <strong>$filter<\/strong>, <strong>$top<\/strong>, <strong>$skip<\/strong>, <strong>$orderby<\/strong> and <strong>$count<\/strong> on collection property selection.<\/p>\n<p>For example, we can select the collection property of string as:<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(2)?$select=Emails<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(Emails)\/$entity\",\r\n    \"Emails\": [\r\n        \"E8\",\r\n        \"E7\",\r\n        \"E9\"\r\n    ]\r\n}\r\n<\/pre>\n<ul>\n<li>Now, we can add nested <strong>$filter<\/strong> as:<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(2)?$select=Emails($filter=$it eq 'E7')<\/pre>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(Emails)\/$entity\",\r\n    \"Emails\": [\r\n        \"E7\"\r\n    ]\r\n}\r\n<\/pre>\n<ul>\n<li>We can add nested <strong>$top<\/strong>, <strong>$skip<\/strong> as:<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(2)?$select=Emails($top=1;$skip=1)<\/pre>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(Emails)\/$entity\",\r\n    \"Emails\": [\r\n        \"E7\"\r\n    ]\r\n}\r\n<\/pre>\n<ul>\n<li>Also, we can add <strong>$orderby<\/strong> as:<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(2)?$select=Emails($top=2;$skip=1;$orderby=$it)<\/pre>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(Emails)\/$entity\",\r\n    \"Emails\": [\r\n        \"E8\",\r\n        \"E9\"\r\n    ]\r\n}\r\n<\/pre>\n<p>Or order by in <em><strong>descending<\/strong> <\/em>order as:<\/p>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(2)?$select=Emails($top=2;$skip=1;$orderby=$it desc)\r\n<\/pre>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(Emails)\/$entity\",\r\n    \"Emails\": [\r\n        \"E8\",\r\n        \"E7\"\r\n    ]\r\n}\r\n<\/pre>\n<p>The above query options can also apply to complex type collection property, for example:<\/p>\n<ul>\n<li><strong>$filter<\/strong> on collection complex property<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(2)?$select=FavoriteAddresses($filter=Street eq '32ST NE')<\/pre>\n<pre class=\"lang:default decode:true \">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(FavoriteAddresses)\/$entity\",\r\n    \"FavoriteAddresses\": [\r\n        {\r\n            \"Street\": \"32ST NE\",\r\n            \"City\": \"Bellewe\"\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<ul>\n<li><strong>$top<\/strong>, <strong>$skip<\/strong>, <strong>$count<\/strong>, <strong>$orderby<\/strong> on collection complex property<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(2)?$select=FavoriteAddresses($top=2;$skip=1;$count=true;$orderby=City desc)<\/pre>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(FavoriteAddresses)\/$entity\",\r\n    \"FavoriteAddresses@odata.count\": 3,\r\n    \"FavoriteAddresses\": [\r\n        {\r\n            \"@odata.type\": \"#SelectImprovement.Models.BillAddress\",\r\n            \"Street\": \"Main ST\",\r\n            \"City\": \"Issaue\",\r\n            \"FirstName\": \"Peter\",\r\n            \"LastName\": \"Jok\"\r\n        },\r\n        {\r\n            \"Street\": \"32ST NE\",\r\n            \"City\": \"Bellewe\"\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p><strong>Note:<\/strong> So far, <strong>$filter<\/strong>, <strong>$top<\/strong>, <strong>$skip<\/strong> and <strong>$orderby<\/strong> work for &#8220;all&#8221; type collection structural property, such as primitive type, Enum type and complex type. However, <strong>$count<\/strong> only works for complex type collection property.<\/p>\n<h4>Nested $expand in $select (?)<\/h4>\n<p>It also supports to expand the navigation property under a complex property. For example, we can get a customer with \u201c<strong>HomeAddress<\/strong>\u201d selected meanwhile \u201c<strong>ZipCode<\/strong>\u201d is included under <strong>HomeAddress<\/strong> property.<\/p>\n<p>It seems that we can use the \u201c<strong>nested $expand<\/strong>\u201d in selection, same as nested <strong>$filter<\/strong> as above, like:<\/p>\n<pre class=\"lang:default decode:true\">~\/Customers(2)?$select=HomeAddress($expand=ZipCode)<\/pre>\n<p>However, it\u2019s not allowed, not supported by design. Even though the OData spec says that you may use \u201c<strong>$select with nested $expand<\/strong>\u201d, but there is no compelling use case for such a scenario. There&#8217;s active discussion around this topic and this might change in the near future. So, at this time, we decided not to provide support for nested <strong>$expand<\/strong> in <strong>$select<\/strong>.<\/p>\n<p>However, it doesn\u2019t mean that we cannot accomplish the above goal. We can combine <strong>$select<\/strong> and <strong>$expand<\/strong> together to get the result.<\/p>\n<p>Let&#8217;s start from the expand path. Simply put, we can use the expand path to expand a navigation property under a complex property as below:<\/p>\n<pre class=\"lang:default decode:true \">http:\/\/localhost:5000\/odata\/Customers(1)?$expand=HomeAddress\/ZipCode<\/pre>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress\/ZipCode())\/$entity\",\r\n    \"Id\": 1,\r\n    \"Name\": \"Balmy\",\r\n    \"Emails\": [\r\n        \"E1\",\r\n        \"E3\",\r\n        \"E2\"\r\n    ],\r\n    \"HomeAddress\": {\r\n        \"Street\": \"145TH AVE\",\r\n        \"City\": \"Redonse\",\r\n        \"ZipCode\": {\r\n            \"Id\": 71,\r\n            \"DisplayName\": \"aebc\"\r\n        }\r\n    },\r\n    \"FavoriteAddresses\": [\r\n        {\r\n            \"Street\": \"145TH AVE\",\r\n            \"City\": \"Redonse\"\r\n        },\r\n        {\r\n            \"@odata.type\": \"#SelectImprovement.Models.BillAddress\",\r\n            \"Street\": \"Main ST\",\r\n            \"City\": \"Issaue\",\r\n            \"FirstName\": \"Peter\",\r\n            \"LastName\": \"Jok\"\r\n        },\r\n        {\r\n            \"Street\": \"32ST NE\",\r\n            \"City\": \"Bellewe\"\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p>We can see \u201c<strong>ZipCode<\/strong>\u201d navigation property included under \u201c<strong>HomeAddress<\/strong>\u201d.<\/p>\n<p>We can also use the same pattern on the navigation property of collection complex property as:<\/p>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(1)?$expand=FavoriteAddresses\/ZipCode<\/pre>\n<p>We can get the following result:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(FavoriteAddresses\/ZipCode())\/$entity\",\r\n    \"Id\": 1,\r\n    \"Name\": \"Balmy\",\r\n    \"Emails\": [\r\n        \"E1\",\r\n        \"E3\",\r\n        \"E2\"\r\n    ],\r\n    \"HomeAddress\": {\r\n        \"Street\": \"145TH AVE\",\r\n        \"City\": \"Redonse\"\r\n    },\r\n    \"FavoriteAddresses\": [\r\n        {\r\n            \"Street\": \"145TH AVE\",\r\n            \"City\": \"Redonse\",\r\n            \"ZipCode\": {\r\n                \"Id\": 71,\r\n                \"DisplayName\": \"aebc\"\r\n            }\r\n        },\r\n        {\r\n            \"@odata.type\": \"#SelectImprovement.Models.BillAddress\",\r\n            \"Street\": \"Main ST\",\r\n            \"City\": \"Issaue\",\r\n            \"FirstName\": \"Peter\",\r\n            \"LastName\": \"Jok\",\r\n            \"ZipCode\": {\r\n                \"Id\": 61,\r\n                \"DisplayName\": \"yxbc\"\r\n            }\r\n        },\r\n        {\r\n            \"Street\": \"32ST NE\",\r\n            \"City\": \"Bellewe\",\r\n            \"ZipCode\": {\r\n                \"Id\": 81,\r\n                \"DisplayName\": \"bexc\"\r\n            }\r\n        }\r\n    ]\r\n}\r\n<\/pre>\n<p>However, we cannot use nested <strong>$expand<\/strong> in this scenario as below:<\/p>\n<pre class=\"lang:default decode:true\">~\/Customers(1)?$expand=FavoriteAddresses($expand=ZipCode)<\/pre>\n<p>A <strong>$expand<\/strong> query as such will get an error message stating that \u201c<strong>FavoriteAddresses<\/strong>\u201d is not a navigation property and you can\u2019t expand on a non-navigation property.<\/p>\n<p>To summarize, the expand path should follow up the following basic rules:<\/p>\n<ul>\n<li><span style=\"font-size: 12pt;\">The last segment in the expand path should be <strong>navigation property<\/strong> segment.<\/span><\/li>\n<li><span style=\"font-size: 12pt;\">The other segment could be <strong>complex property<\/strong> segment or <strong>type cast<\/strong> segment.<\/span><\/li>\n<\/ul>\n<h4>Combine $select and $expand<\/h4>\n<p>Even though we cannot use <strong>$expand<\/strong> in <strong>$select<\/strong>, we can combine them together to get more interesting results, for example:<\/p>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(1)?$select=Name&amp;$expand=HomeAddress\/ZipCode<\/pre>\n<p>We can get:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(Name,HomeAddress\/ZipCode())\/$entity\",\r\n    \"Name\": \"Balmy\",\r\n    \"HomeAddress\": {\r\n        \"Street\": \"145TH AVE\",\r\n        \"City\": \"Redonse\",\r\n        \"ZipCode\": {\r\n            \"Id\": 71,\r\n            \"DisplayName\": \"aebc\"\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>You may notice that in this scenario, I only select \u201c<strong>Name<\/strong>\u201d explicitly, without select \u201c<strong>HomeAddress<\/strong>\u201d, and only expand \u201c<strong>ZipCode<\/strong>\u201d in \u201c<strong>HomeAddress<\/strong>\u201d, the payload should only include \u201c<strong>Name<\/strong>\u201d property and \u201c<strong>ZipCode<\/strong>\u201d of \u201c<strong>HomeAddress<\/strong>\u201d. The above payload does not seem correct. It&#8217;s a known issue and will be fixed in the future release.<\/p>\n<p>To get the result only including \u201c<strong>ZipCode<\/strong>\u201d of \u201c<strong>HomeAddress<\/strong>\u201d, we can construct a query like:<\/p>\n<pre class=\"lang:default decode:true\">http:\/\/localhost:5000\/odata\/Customers(1)?$select=HomeAddress\/ZipCode&amp;$expand=HomeAddress\/ZipCode<\/pre>\n<p>which gets:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n    \"@odata.context\": \"http:\/\/localhost:5000\/odata\/$metadata#Customers(HomeAddress\/ZipCode,HomeAddress\/ZipCode())\/$entity\",\r\n    \"HomeAddress\": {\r\n        \"ZipCode\": {\r\n            \"Id\": 71,\r\n            \"DisplayName\": \"aebc\"\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p><strong>Note<\/strong>, in &#8220;<strong>Full<\/strong>&#8221; metadata level, the payload includes the navigation link metadata.<\/p>\n<h4>Summary<\/h4>\n<p>Thanks for reading this article. I hope you enjoy the new <strong>$select<\/strong> usages released in new ASP.NET Core OData. If you have any questions, comments, concerns, please feel free send email to <a href=\"emailto: saxu@microsoft.com\">saxu@microsoft.com. <\/a>\u00a0If you find any issues or have a feature request, please go to <a href=\"https:\/\/github.com\/OData\">odata@github<\/a>.<\/p>\n<p>For the sample project used in this article, you can find it <a href=\"https:\/\/github.com\/xuzhg\/MyAspNetCore\/tree\/master\/src\/SelectImprovement\">here<\/a>.\u00a0And big thanks for reviewing from <span style=\"font-size: 1rem;\"><a href=\"emailto: Saurabh.Madan@microsoft.com\">Saurahb Madan<\/a>.<\/span><\/p>\n<div>\n<div class=\"ts-tab-bar-wrapper\" data-tid=\"tabs-menu\">\n<div class=\"ts-add-tab-button-wrapper\" data-tid=\"addTabButtonWrapper\"><\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The release of ASP.NET Core OData v7.3 brings a ton of improvements to $select functionality. In this article, I\u2019d like to introduce some of the new features of $select and its usages in combination with other query options like $filter, $top, $skip, $orderby, $count and $expand. This tutorial assumes that you already have the knowledge [&hellip;]<\/p>\n","protected":false},"author":514,"featured_media":3564,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,116,117],"tags":[502,48,80],"class_list":["post-3537","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata","category-odl","category-webapi","tag-asp-net-core","tag-odata","tag-web-api"],"acf":[],"blog_post_summary":"<p>The release of ASP.NET Core OData v7.3 brings a ton of improvements to $select functionality. In this article, I\u2019d like to introduce some of the new features of $select and its usages in combination with other query options like $filter, $top, $skip, $orderby, $count and $expand. This tutorial assumes that you already have the knowledge [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/3537","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/users\/514"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/comments?post=3537"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/3537\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media\/3564"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media?parent=3537"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=3537"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=3537"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}