[BUG] Function sample for deferred toBusinessPartner call fails
WillEastbury opened this issue · 12 comments
Is there an existing issue for this?
- I have searched the existing issues
Type of issue
bug
Describe the bug
Transferred from old repo #20
Fetching address with deferred attribute fails with internal server error. Message doesn't show up on APIM or SAP GW.
Expected Behavior
Should defer the fetch.
Steps To Reproduce
var AddressCity = (await salesOrderInput.ToBusinessPartner.GetAsync()).Address.City;
Add screenshots to help explain your problem
No response
Additional context
No response
I will take this one.
In flight - Looking at this now.
I'm having difficulty replicating this.
I've added 2 lines to the console test output
// get me a sales order line item with id 0500000000 and pull the city asynchronously from the business partner
var salesOrderInput = await sos.GetAsync("0500000000");
_logger.LogInformation((await salesOrderInput.ToBusinessPartner.GetAsync()).Address.City);
And it correctly seems to output "GWSAMPLE_BASIC.TestService: Information: Walldorf", which is what I would expect.
I'll check the functions sample next.
Right I got it - the Dispatcher is throwing a null reference exception in the GetAsync method call from L57 of the Deferred Class
Result = await DispatchThroughDeferredURL(__deferred.uri);
But it only seems to happen in Functions weirdly - I'm on it.
I think I may have found the problem. Functions must be internally serializing the bound object and we have these attributes present on the object internally.
[Newtonsoft.Json.JsonIgnore()]
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public IOperationsDispatcher Dispatcher {protected internal get; set; }
Which means serialization is dropping the context's attachment
Or it's not getting set in the first place by the Binding. If you access the Sales Order outside the binding, like this
var salesOrderInput2 = await sos.GetAsync(ID);
var bp = await salesOrderInput2.ToBusinessPartner.GetAsync();
It works fine.
OK, I have located the issue. In the wire up of the reader in the function binding we call the dispatcher objects directly instead of going through the ODataEntitySetOperations class's GetAsync method (which calls AttachDispatcher).
The reason we did this I think was to try and save some complexity in knowing which ODataEntitySetOperations to implement in the binding method.
I think an ODataEntitySetOperationsFactory method might be appropriate, then I can fix it in the codegen stage
I can add this
public interface IQuerySetOperationsFactory
{
IQuerySetOperations<T> Create<T>() where T : IBaseDTOWithIDAndETag;
}
// This is the factory that is injected into the DI container to create the IODataEntitySetOperations<T> for each type of DTO
public class ODataEntitySetOperationsFactory : IQuerySetOperationsFactory
{
private IOperationsDispatcher _dispatcher;
public ODataEntitySetOperationsFactory(IOperationsDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public IQuerySetOperations<T> Create<T>() where T : IBaseDTOWithIDAndETag
{
return new ODataEntitySetOperations<T>(_dispatcher);
}
}
And instantiate that in the ConfigureBindings Method
IQuerySetOperationsFactory esops = new ODataEntitySetOperationsFactory(dispatcher);
Then we can change the ConfigureBindings methods from
context.BindToInput<Input_GWSAMPLE_BASIC_BusinessPartnerAttribute, BusinessPartner>((x) => dispatcher.GetAsync<BusinessPartner>(x.BusinessPartnerID).Result);
To
context.BindToInput<Input_GWSAMPLE_BASIC_SalesOrderAttribute, SalesOrder>((x) => esops.Create<SalesOrder>().GetAsync(x.SalesOrderID).Result);
I've also added an extra function to test this feature
Calling http://localhost:7071/api/GetDeliveryAddressLabel/0500000000 on the sample should render a dispatch label.
See the image above
Checking in now.