Cookie Consent by Free Privacy Policy Generator ๐Ÿ“Œ $select Enhancement in ASP.NET Core OData

๐Ÿ  Team IT Security News

TSecurity.de ist eine Online-Plattform, die sich auf die Bereitstellung von Informationen,alle 15 Minuten neuste Nachrichten, Bildungsressourcen und Dienstleistungen rund um das Thema IT-Sicherheit spezialisiert hat.
Ob es sich um aktuelle Nachrichten, Fachartikel, Blogbeitrรคge, Webinare, Tutorials, oder Tipps & Tricks handelt, TSecurity.de bietet seinen Nutzern einen umfassenden รœberblick รผber die wichtigsten Aspekte der IT-Sicherheit in einer sich stรคndig verรคndernden digitalen Welt.

16.12.2023 - TIP: Wer den Cookie Consent Banner akzeptiert, kann z.B. von Englisch nach Deutsch รผbersetzen, erst Englisch auswรคhlen dann wieder Deutsch!

Google Android Playstore Download Button fรผr Team IT Security



๐Ÿ“š $select Enhancement in ASP.NET Core OData


๐Ÿ’ก Newskategorie: Programmierung
๐Ÿ”— Quelle: devblogs.microsoft.com

The release of ASP.NET Core OData v7.3 brings a ton of improvements to $select functionality. In this article, Iโ€™d 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 to build an ASP.NET Core Web Application service using ASP.NET Core OData NuGget package. If not, start by reading ASP.NET Core OData now Available and refer to the sample project used in this article.
Letโ€™s get started.

Data Model

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.

Below are the CLR class types used in sample project:

// Entity type
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<string> Emails { get; set; }
    public Address HomeAddress { get; set; }
    public IList<Address> FavoriteAddresses { get; set; }
    public Order PersonOrder { get; set; }
    public Order[] Orders { get; set; }
}

// Complex type
public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public ZipCode ZipCode { get; set; }
}

// Complex type
public class BillAddress : Address
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

// Entity type
public class Order
{
    public int Id { get; set; }
    public string Title { get; set; }
}

// Entity Type
public class ZipCode
{
    public int Id { get; set; }
    public string DisplayName { get; set; }
}

Where,

  • Type โ€œCustomerโ€, โ€œOrderโ€, โ€œZipCodeโ€ serve as Edm entity types.
  • Type โ€œAddressโ€ and โ€œBillAddressโ€ serve as Edm complex types, and โ€œBillAddressโ€ is derived from โ€œAddressโ€.
  • โ€œAddressโ€ has a navigation property named โ€œZipCodeโ€œ.

In the corresponding Edm model, I have โ€œCustomersโ€, โ€œOrdersโ€ and โ€œZipCodesโ€ as the Edm entity sets related to the above Edm types.

Besides, I have two real โ€œCustomersโ€ named โ€œBalmyโ€ and โ€œChillyโ€ in the sample project. For other propertiesโ€™ information, you can refer the source code or build, run, and send a http://localhost:5000/odata/Customers to get.

$select

$select is one of OData supported query options which allows the clients to select specific properties from the server. The biggest advantage of using $select is that the heavy lifting is done by the server before the data is returned, which leads to better performance. Hassan Habitโ€™s Optimizing Web Applications with OData $Select shares some advantages to use $select.

For example, we can select a complex property using $select like:

http://localhost:5000/odata/Customers(1)?$select=HomeAddress

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress)/$entity",
    "HomeAddress": {
        "Street": "145TH AVE",
        "City": "Redonse"
    }
}

In this way, we can get data performance by limiting the result only including the properties wanted. That is, server doesnโ€™t need to return all properties belong to โ€œCustomer(1)โ€, meanwhile the client doesnโ€™t need to trim the result.

Select path in $select

The above example is a very basic usage of $select. With the release of ASP.NET Core OData v7.3.0, we now have support to use select path in $select.

For example:

http://localhost:5000/odata/Customers(1)?$select=HomeAddress/Street

In summary, the select path should follow up the following basic rules:

  • If only one segment exists, it could be โ€œ*โ€, โ€œNS.*โ€, โ€œStructural propertyโ€ segment or โ€œNavigation propertyโ€ segment.
  • Otherwise, the last segment in a select path could be โ€œStructural propertyโ€ segment or โ€œNavigation propertyโ€ segment, and the other segments could be โ€œComplex propertyโ€ segment or โ€œComplex type castโ€ segment.

For the above request, we can get the following result:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress/Street)/$entity",
    "HomeAddress": {
        "Street": "145TH AVE"
    }
}

Note: The result only includes the Street in HomeAddress.

Type cast select path in $select

It also supports the type cast in the select path, for example:

http://localhost:5000/odata/Customers?$select=HomeAddress/SelectImprovement.Models.BillAddress/FirstName

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress/SelectImprovement.Models.BillAddress/FirstName)",
    "value": [
        {
            "HomeAddress": {}
        },
        {
            "HomeAddress": {
                "@odata.type": "#SelectImprovement.Models.BillAddress",
                "FirstName": "Peter"
            }
        }
    ]
}

Note:

  • The first customerโ€™s HomeAddress is not a BillAddress, so the entity for this customer only includes the HomeAddress property with empty object.
  • The second customerโ€™s HomeAddress is a BillAddress, so it includes the selected property named FirstName and a control metadata property named @odata.type.

Nested $select in $select

We can use the nested $select to replace the above select path. In fact, Itโ€™s more understandable to use nested $select.

A simplified example should look as follows:

http://localhost:5000/odata/Customers?$select=HomeAddress($select=Street)

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress)",
    "value": [
        {
            "HomeAddress": {
                "Street": "145TH AVE"
            }
        },
        {
            "HomeAddress": {
                "@odata.type": "#SelectImprovement.Models.BillAddress",
                "Street": "Main ST"
            }
        }
    ]
}

Note, the context URI in this scenario is not correct. It should be same as the context URI in select path scenario. Itโ€™s a known issue and will be fixed in the future release.

Select sub navigation property in $select

It also supports to select the sub navigation property in a complex property. For example:

http://localhost:5000/odata/Customers?$select=HomeAddress/ZipCode

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress/ZipCode)",
    "value": [
        {
            "HomeAddress": {}
        },
        {
            "HomeAddress": {
                "@odata.type": "#SelectImprovement.Models.BillAddress"
            }
        }
    ]
}

You may be wondering โ€œWhy the HomeAddress is empty object here?โ€ Itโ€™s empty not because itโ€™s not a BillAddress, but because the navigation link control information is omitted by default in the โ€œMinimalโ€ metadata level. In OData, if we donโ€™t set the metadata level, by default itโ€™s โ€œMinimalโ€ metadata level. So, if we want to get all control metadata information, we can use $format to set the โ€œFullโ€ metadata level as below:

http://localhost:5000/odata/Customers(1)?$select=HomeAddress/ZipCode&$format=application/json;odata.metadata=full

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress/ZipCode)/$entity",
    "@odata.type": "#SelectImprovement.Models.Customer",
    "@odata.id": "http://localhost:5000/odata/Customers(1)",
    "@odata.editLink": "http://localhost:5000/odata/Customers(1)",
    "HomeAddress": {
        "@odata.type": "#SelectImprovement.Models.Address",
        "[email protected]": "http://localhost:5000/odata/Customers(1)/HomeAddress/ZipCode/$ref",
        "[email protected]": "http://localhost:5000/odata/Customers(1)/HomeAddress/ZipCode"
    }
}

Again, we can use nested $select to get the same payload result as below (except the context URI):

http://localhost:5000/odata/Customers(1)?$select=HomeAddress($select=ZipCode)&$format=application/json;odata.metadata=full

Selection on collection property

Now, thereโ€™s support to select path and nested select on collection complex property. For example:

http://localhost:5000/odata/Customers(2)?$select=FavoriteAddresses/Street
or
http://localhost:5000/odata/Customers(2)?$select=FavoriteAddresses($select=Street)

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(FavoriteAddresses/Street)/$entity",
    "FavoriteAddresses": [
        {
            "Street": "145TH AVE"
        },
        {
            "@odata.type": "#SelectImprovement.Models.BillAddress",
            "Street": "Main ST"
        },
        {
            "Street": "32ST NE"
        }
    ]
}

Note: The result in select path and nested select is almost same except the context URI.

Nested $filter, $top, $skip, $orderby, $count

Besides the nested $select, there is support for nested $filter, $top, $skip, $orderby and $count on collection property selection.

For example, we can select the collection property of string as:

http://localhost:5000/odata/Customers(2)?$select=Emails

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(Emails)/$entity",
    "Emails": [
        "E8",
        "E7",
        "E9"
    ]
}

  • Now, we can add nested $filter as:

http://localhost:5000/odata/Customers(2)?$select=Emails($filter=$it eq 'E7')

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(Emails)/$entity",
    "Emails": [
        "E7"
    ]
}

  • We can add nested $top, $skip as:

http://localhost:5000/odata/Customers(2)?$select=Emails($top=1;$skip=1)

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(Emails)/$entity",
    "Emails": [
        "E7"
    ]
}

  • Also, we can add $orderby as:

http://localhost:5000/odata/Customers(2)?$select=Emails($top=2;$skip=1;$orderby=$it)

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(Emails)/$entity",
    "Emails": [
        "E8",
        "E9"
    ]
}

Or order by in descending order as:

http://localhost:5000/odata/Customers(2)?$select=Emails($top=2;$skip=1;$orderby=$it desc)

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(Emails)/$entity",
    "Emails": [
        "E8",
        "E7"
    ]
}

The above query options can also apply to complex type collection property, for example:

  • $filter on collection complex property

http://localhost:5000/odata/Customers(2)?$select=FavoriteAddresses($filter=Street eq '32ST NE')

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(FavoriteAddresses)/$entity",
    "FavoriteAddresses": [
        {
            "Street": "32ST NE",
            "City": "Bellewe"
        }
    ]
}

  • $top, $skip, $count, $orderby on collection complex property

http://localhost:5000/odata/Customers(2)?$select=FavoriteAddresses($top=2;$skip=1;$count=true;$orderby=City desc)

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(FavoriteAddresses)/$entity",
    "[email protected]": 3,
    "FavoriteAddresses": [
        {
            "@odata.type": "#SelectImprovement.Models.BillAddress",
            "Street": "Main ST",
            "City": "Issaue",
            "FirstName": "Peter",
            "LastName": "Jok"
        },
        {
            "Street": "32ST NE",
            "City": "Bellewe"
        }
    ]
}

Note: So far, $filter, $top, $skip and $orderby work for โ€œallโ€ type collection structural property, such as primitive type, Enum type and complex type. However, $count only works for complex type collection property.

Nested $expand in $select (?)

It also supports to expand the navigation property under a complex property. For example, we can get a customer with โ€œHomeAddressโ€ selected meanwhile โ€œZipCodeโ€ is included under HomeAddress property.

It seems that we can use the โ€œnested $expandโ€ in selection, same as nested $filter as above, like:

~/Customers(2)?$select=HomeAddress($expand=ZipCode)

However, itโ€™s not allowed, not supported by design. Even though the OData spec says that you may use โ€œ$select with nested $expandโ€, but there is no compelling use case for such a scenario. Thereโ€™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 $expand in $select.

However, it doesnโ€™t mean that we cannot accomplish the above goal. We can combine $select and $expand together to get the result.

Letโ€™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:

http://localhost:5000/odata/Customers(1)?$expand=HomeAddress/ZipCode

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress/ZipCode())/$entity",
    "Id": 1,
    "Name": "Balmy",
    "Emails": [
        "E1",
        "E3",
        "E2"
    ],
    "HomeAddress": {
        "Street": "145TH AVE",
        "City": "Redonse",
        "ZipCode": {
            "Id": 71,
            "DisplayName": "aebc"
        }
    },
    "FavoriteAddresses": [
        {
            "Street": "145TH AVE",
            "City": "Redonse"
        },
        {
            "@odata.type": "#SelectImprovement.Models.BillAddress",
            "Street": "Main ST",
            "City": "Issaue",
            "FirstName": "Peter",
            "LastName": "Jok"
        },
        {
            "Street": "32ST NE",
            "City": "Bellewe"
        }
    ]
}

We can see โ€œZipCodeโ€ navigation property included under โ€œHomeAddressโ€.

We can also use the same pattern on the navigation property of collection complex property as:

http://localhost:5000/odata/Customers(1)?$expand=FavoriteAddresses/ZipCode

We can get the following result:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(FavoriteAddresses/ZipCode())/$entity",
    "Id": 1,
    "Name": "Balmy",
    "Emails": [
        "E1",
        "E3",
        "E2"
    ],
    "HomeAddress": {
        "Street": "145TH AVE",
        "City": "Redonse"
    },
    "FavoriteAddresses": [
        {
            "Street": "145TH AVE",
            "City": "Redonse",
            "ZipCode": {
                "Id": 71,
                "DisplayName": "aebc"
            }
        },
        {
            "@odata.type": "#SelectImprovement.Models.BillAddress",
            "Street": "Main ST",
            "City": "Issaue",
            "FirstName": "Peter",
            "LastName": "Jok",
            "ZipCode": {
                "Id": 61,
                "DisplayName": "yxbc"
            }
        },
        {
            "Street": "32ST NE",
            "City": "Bellewe",
            "ZipCode": {
                "Id": 81,
                "DisplayName": "bexc"
            }
        }
    ]
}

However, we cannot use nested $expand in this scenario as below:

~/Customers(1)?$expand=FavoriteAddresses($expand=ZipCode)

A $expand query as such will get an error message stating that โ€œFavoriteAddressesโ€ is not a navigation property and you canโ€™t expand on a non-navigation property.

To summarize, the expand path should follow up the following basic rules:

  • The last segment in the expand path should be navigation property segment.
  • The other segment could be complex property segment or type cast segment.

Combine $select and $expand

Even though we cannot use $expand in $select, we can combine them together to get more interesting results, for example:

http://localhost:5000/odata/Customers(1)?$select=Name&$expand=HomeAddress/ZipCode

We can get:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(Name,HomeAddress/ZipCode())/$entity",
    "Name": "Balmy",
    "HomeAddress": {
        "Street": "145TH AVE",
        "City": "Redonse",
        "ZipCode": {
            "Id": 71,
            "DisplayName": "aebc"
        }
    }
}

You may notice that in this scenario, I only select โ€œNameโ€ explicitly, without select โ€œHomeAddressโ€, and only expand โ€œZipCodeโ€ in โ€œHomeAddressโ€, the payload should only include โ€œNameโ€ property and โ€œZipCodeโ€ of โ€œHomeAddressโ€. The above payload does not seem correct. Itโ€™s a known issue and will be fixed in the future release.

To get the result only including โ€œZipCodeโ€ of โ€œHomeAddressโ€, we can construct a query like:

http://localhost:5000/odata/Customers(1)?$select=HomeAddress/ZipCode&$expand=HomeAddress/ZipCode

which gets:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(HomeAddress/ZipCode,HomeAddress/ZipCode())/$entity",
    "HomeAddress": {
        "ZipCode": {
            "Id": 71,
            "DisplayName": "aebc"
        }
    }
}

Note, in โ€œFullโ€ metadata level, the payload includes the navigation link metadata.

Summary

Thanks for reading this article. I hope you enjoy the new $select usages released in new ASP.NET Core OData. If you have any questions, comments, concerns, please feel free send email to [email protected]. ย If you find any issues or have a feature request, please go to odata@github.

For the sample project used in this article, you can find it here.ย And big thanks for reviewing from Saurahb Madan.

The post $select Enhancement in ASP.NET Core OData appeared first on OData.

...



๐Ÿ“Œ Using SkipToken for Paging in Asp.Net OData and Asp.Net Core OData


๐Ÿ“ˆ 83.66 Punkte

๐Ÿ“Œ $select Enhancement in ASP.NET Core OData


๐Ÿ“ˆ 82.34 Punkte

๐Ÿ“Œ Visual Studio for Mac + ASP.NET Core โ€“ Getting started with ASP.NET Core using eShopOnWeb


๐Ÿ“ˆ 46.13 Punkte

๐Ÿ“Œ Experimenting with OData in ASP.NET Core 3.1


๐Ÿ“ˆ 44.95 Punkte

๐Ÿ“Œ Aggregation extensions in OData ASP.NET Core


๐Ÿ“ˆ 44.95 Punkte

๐Ÿ“Œ Migrating OData V3 Services to OData V4 without Disrupting Existing Clients


๐Ÿ“ˆ 43.78 Punkte

๐Ÿ“Œ Microsoft .NET Maze: Understand .NET Core Vs .NET Framework Vs ASP.NET


๐Ÿ“ˆ 42.01 Punkte

๐Ÿ“Œ .NET Core, ASP.NET Core und Entity Framework Core 2.1 sind fertig


๐Ÿ“ˆ 41.88 Punkte

๐Ÿ“Œ what are competitive alternatives of .NET Core, ASP.NET Core, and EF Core for development/deployment on Linux?


๐Ÿ“ˆ 41.88 Punkte

๐Ÿ“Œ Upcoming SameSite Cookie Changes in ASP.NET and ASP.NET Core


๐Ÿ“ˆ 39.89 Punkte

๐Ÿ“Œ Microsoft legt Bug-Bounty-Programm fรผr .NET Core und ASP.NET Core neu auf


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft legt Bug-Bounty-Programm fรผr .NET Core und ASP.NET Core neu auf


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core Web Request Spoofing [CVE-2017-0256]


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core 3.0 lรคuft nur noch auf .NET Core


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ .NET Core und ASP.NET Core 2.2.0 Preview 3 verfรผgbar


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 6


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 7


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core Web Request erweiterte Rechte


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 8


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ 4021279 - Vulnerabilities in .NET Core, ASP.NET Core Could Allow Elevation of Privilege - Version: 1.1


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 9


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core Web Request spoofing


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core Web Request privilege escalation


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core Web Request denial of service


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core updates in .NET Core 3.1 Preview 2


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core Web Request Denial of Service


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core and Blazor updates in .NET Core 3.0 Release Candidate 1


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core and Blazor updates in .NET Core 3.0


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Updating an ASP.NET Core 2.2 Web Site to .NET Core 3.1 LTS


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Microsoft ASP.NET Core/.NET Core System.IO.Pipelines denial of service


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Announcing a Microsoft .NET Core and ASP.NET Core Bug Bounty


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core updates in .NET Core 3.1 Preview 1


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core updates in .NET Core 3.1 Preview 3


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ ASP.NET Core updates in .NET Core 3.1


๐Ÿ“ˆ 35.63 Punkte

๐Ÿ“Œ Optimizing Web Applications with OData $Select


๐Ÿ“ˆ 35.53 Punkte











matomo