Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select only OData child elements

Tags:

c#

odata

I'm building an OData application and I'm struggling on how to retrieve results and only include certain (child properties).

First, let me show you the registration in my builder:

builder.EntitySet<AggregatedArticlesSearchModel>("Search").EntityType.HasKey(x => x.Name);

Now, on to the model that I'm returning from my Query:

<EntityType Name="AggregatedArticlesSearchModel">
    <Key>
        <PropertyRef Name="Name"/>
    </Key>
    <Property Name="Name" Nullable="false" Type="Edm.String"/>
    <Property Name="Values" Type="Collection(Zevij_Necomij.Mobile.App.Api.Models.OccurenceViewModel)"/>
</EntityType>
<ComplexType Name="OccurenceViewModel">
    <Property Name="Value" Type="Edm.String"/>
    <Property Name="Count" Nullable="false" Type="Edm.Double"/>
    <Property Name="Articles" Type="Collection(Zevij_Necomij.Mobile.App.Api.Models.AggregatedArticleDescriptionViewModel)"/>
</ComplexType>
<ComplexType Name="AggregatedArticleDescriptionViewModel">
    <Property Name="Name" Type="Edm.String"/>
    <Property Name="Specification" Type="Edm.String"/>
    <Property Name="Brand" Type="Edm.String"/>
</ComplexType>

When I'm executing a request to get the data, I'm not doing anything fancy but just returning the results from the database:

public async Task<IHttpActionResult> Get()
{
    // Create all the managers for the platform context that are required by the application.
    var classificationManager = Context.CreateManager(typeof(AggregatedArticleManager<>)) as AggregatedArticleManager<IAggregatedArticleStore<AggregatedArticle>>;

    var classifications = await classificationManager.GetAllAsync();

    var returnList = classifications.OrderBy(x => x.Name).Select(AggregatedArticlesSearchModel.MapFromDbModel).ToList();

    return Ok(returnList.AsQueryable());
}

Since I'm working with child objects, the list can get quite huge:

{
  "@odata.context":     "http://api.mobileapp.appserver.dev.dsoft.be/OData/$metadata#Search",
  "value": [
    {
      "Name": "(Veiligheids)slipkoppeling",
      "Values": [
        {
          "Value": "ja",
          "Count": 118,
      "Articles": [
        {
          "Name": "230 V Sleuvenzaag",
          "Specification": "Compacte machine",
          "Brand": "Makita"
        },
        {
          "Name": "230V Cirkelzaag SJS",
          "Specification": "Softstart voor
        },
    }
}

I can have a thousand articles in a single set, thus, way to much to return over the Web Api. Since I don't need all those properties in a single request, I was thinking to let the cliënt only retrieve child properties by using the ?$select parameter, thus the cliënts can say for example:

OData/Search?$select=Values

The problem here is that I for example, only want to return the Count, thus, I tought that a request like this was possible:

OData/Search?$select=Values/Count

However, this leads to an OData error: "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."

Anyone who has an idea on how to solve this one?

like image 204
Complexity Avatar asked Mar 15 '23 17:03

Complexity


1 Answers

@Complexity

As I know, to select a sub property in a property is not supported.

However, if you build the OccurenceViewModel as entity type, then you can use the nested $select in $expand to meet your requirement.

For example:

1) build the entity type

builder.EntitySet<OccurenceViewModel>("ViewModels").EntityType.HasKey(x => x.Value);

then, Values in AggregatedArticlesSearchModel should be a navigation property.

2) Now, you can issue a GET request as follows to only return the Count property:

GET ~/odata/Search?$select=Values&$expand=Values($select=Count)

Then, the payload should look like the below:

{
  "@odata.context":"http://localhost/odata/$metadata#Search(Values,Values(Count))","value":[
    {
      "Values":[
        {
          "Count":101.0
        },{
          "Count":102.0
        }
      ]
    },{
      "Values":[
        {
          "Count":101.0
        },{
          "Count":102.0
        }
      ]
    }
  ]
}

Hope it can help you. Thanks.

like image 112
Sam Xu Avatar answered Mar 17 '23 06:03

Sam Xu