OData / odata.net

ODataLib: Open Data Protocol - .NET Libraries and Frameworks
https://docs.microsoft.com/odata
Other
687 stars 349 forks source link

Support QueryOptions for Actions #1657

Open ChrisSchaller opened 4 years ago

ChrisSchaller commented 4 years ago

There is a lot of power in the server side in allowing Actions to accept QueryOptions, common scenarios include Create style actions that allow the result graph to be filtered or expanded, but also to allow customised filter experience for and endpoint that might have otherwise been implemented as a Function.

But the client library does not facilitate passing through UriOperationParameter to action queries, even though the underlying ExecuteAsync accepts an array of the base type OperationParameter.

While it makes sense to restrict Function queries to only accept UriOperationParameter because the spec and many implementations support Query Options for Actions, the constructor for DataServiceActionQuery should be relaxed to allow OperationParameter.

What would be more useful is if DataServiceActionQuery supported AddQueryOption.

Assemblies affected

Microsoft.OData.Client 7.6.2

Expected result

DataServiceActionQuery and DataServiceActionQuerySingle should be able to accept both BodyOperationParameter and UriOperationParameter:

public DataServiceActionQuery(DataServiceContext context, string requestUriString, params OperationParameter[] parameters)
public DataServiceActionQuerySingle(DataServiceContext context, string requestUriString, params OperationParameter[] parameters)

Actual result

public DataServiceActionQuery(DataServiceContext context, string requestUriString, params BodyOperationParameter[] parameters)
public DataServiceActionQuerySingle(DataServiceContext context, string requestUriString, params BodyOperationParameter[] parameters)

Additional detail

With these simple changes to the constructor, it would be then easier to implement overrides, extensions or client generation to support AddQueryOption and Expand support methods to DataServiceActionQuery

The following example focuses on supporting $filter query option, but $expand would require a similar workaround.

Originally the following syntax was expected to work:

var query = ClientContext.Sensors.ByKey(this.SensorId)
                                 .GetTriggeredSensors(value, this.Timestamp)
                                 .Where(s => s.VTDisabled == false)
                                 .Where(s => s.Card.CardType.Driver == cloudVirtualCardDriver);

var items = query.Execute().ToList();

This particular example originates from a query that was originally a Function request but there are complications and compatibility issues with some server implementations of DateTimeOffset serialization in UriOperationParameter.

The current work around involved editing the Uri in the BeforeRequest event handler to inject the additional query options:

ClientContext.BuildingRequest += ClientContext_BuildingRequest;
var query = ClientContext.Sensors.ByKey(this.SensorId)
                                 .GetTriggeredSensors(value, this.Timestamp);
       //                        .Where(s => s.VTDisabled == false)
       //                        .Where(s => s.Card.CardType.Driver == cloudVirtualCardDriver);

var items = query.Execute().ToList();
ClientContext.BuildingRequest -= ClientContext_BuildingRequest;
private void ClientContext_BuildingRequest(object sender, Microsoft.OData.Client.BuildingRequestEventArgs e)
{
    if (e.RequestUri.PathAndQuery.Contains("GetTriggeredSensors"))
        e.RequestUri = new System.Uri(e.RequestUri + "?$filter=VTDisabled eq false AND Card/CardType/Driver eq '" + cloudVirtualCardDriver + "'");
}
KanishManuja-MS commented 4 years ago

@ChrisSchaller We don't believe that changing the constructor is the right approach here as the actions can only have body parameters. However, we do agree that we should support composing query options on top of actions or functions. We would be happy to review the changes if you are willing to contribute otherwise we will get this added to our backlog.