OData / odata.net

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

Saving related objects as single changeset not working #3056

Open MitchKuijpers opened 2 months ago

MitchKuijpers commented 2 months ago

When saving as single changesset with two unsaved objects and adding one as a related object. I would expect a call when dependent on id's, however it results in a crash. This only happens when trying to send as jsonbatch

Assemblies affected

8.0.1

Reproduce steps

IContainer _container = CreateContainer();
Bank bank = new Bank()
{
    Id = Guid.NewGuid(),
    Name = "test",
    ChamberOfCommerceNumber = "12345",
    TaxNumber = "NL",
};

BankAccount bankAccount = new BankAccount()
{
    Id = Guid.NewGuid(),
    BankAccountTypeId = BankAccountType.Iban,
    AccountNumber = "12345",
    AccountHolder = "test",
    StartDate = DateTime.Now,
    EndDate = DateTime.Now.AddYears(1),
};
bank.BankAccounts.Add(bankAccount);
bankAccount.Bank = bank;

_container.AddToBanks(bank);
_container.AddRelatedObject(bank, nameof(Bank.BankAccounts), bankAccount);

await _container.SaveChangesAsync(Microsoft.OData.Client.SaveChangesOptions.BatchWithSingleChangeset | Microsoft.OData.Client.SaveChangesOptions.UseJsonBatch);

It should be easy to reproduce with 2 related objects.

Expected result

Would send a request with dependent on id's

Actual result

crash while validating the depends-on-Ids.

[!StackTrace] Microsoft.OData.ODataException HResult=0x80131509 Message=Request Id reference [1] in Uri [http://localhost:1234/services/api/odata/$1/BankAccounts] is not found in effective depends-on-Ids [] of the request. Source=Microsoft.OData.Core StackTrace: at Microsoft.OData.ODataBatchUtils.ValidateReferenceUri(Uri uri, IEnumerable1 dependsOnRequestIds, Uri baseUri) at Microsoft.OData.ODataBatchWriter.BuildOperationRequestMessage(Stream outputStream, String method, Uri uri, String contentId, String groupId, IEnumerable1 dependsOnIds) at Microsoft.OData.Json.ODataJsonBatchWriter.CreateOperationRequestMessageImplementation(String method, Uri uri, String contentId, BatchPayloadUriOption payloadUriOption, IEnumerable1 dependsOnIds) at Microsoft.OData.ODataBatchWriter.CreateOperationRequestMessageInternal(String method, Uri uri, String contentId, BatchPayloadUriOption payloadUriOption, IEnumerable1 dependsOnIds) at Microsoft.OData.ODataBatchWriter.CreateOperationRequestMessage(String method, Uri uri, String contentId, BatchPayloadUriOption payloadUriOption, IEnumerable1 dependsOnIds) at Microsoft.OData.Client.ODataRequestMessageWrapper.CreateBatchPartRequestMessage(ODataBatchWriter batchWriter, BuildingRequestEventArgs requestMessageArgs, RequestInfo requestInfo, String contentId, Boolean isRelativeUri) at Microsoft.OData.Client.BatchSaveResult.CreateRequestMessage(String method, Uri requestUri, HeaderCollection headers, HttpStack httpStack, Descriptor descriptor, String contentId) at Microsoft.OData.Client.BaseSaveResult.CreateRequest(EntityDescriptor entityDescriptor) at Microsoft.OData.Client.BatchSaveResult.GenerateBatchRequest() at Microsoft.OData.Client.BatchSaveResult.BatchBeginRequest() at Microsoft.OData.Client.DataServiceContext.BeginSaveChanges(SaveChangesOptions options, AsyncCallback callback, Object state) at Microsoft.OData.Client.DataServiceContext.<>c__DisplayClass285_02.b0(TArg arg1, AsyncCallback callback, Object state) at System.Threading.Tasks.TaskFactory1.FromAsyncImpl[TArg1](Func4 beginMethod, Func2 endFunction, Action1 endAction, TArg1 arg1, Object state, TaskCreationOptions creationOptions) in //src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs:line 893 at System.Threading.Tasks.TaskFactory1.FromAsync[TArg1](Func4 beginMethod, Func`2 endMethod, TArg1 arg1, Object state) in //src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs:line 829 at Microsoft.OData.Client.DataServiceContext.FromAsync[TArg,TResult](Func4 beginMethod, Func2 endMethod, TArg arg, CancellationToken cancellationToken) at Microsoft.OData.Client.DataServiceContext.SaveChangesAsync(SaveChangesOptions options, CancellationToken cancellationToken) at Microsoft.OData.Client.DataServiceContext.SaveChangesAsync(SaveChangesOptions options) at Nocore.FlexConnector.Application.Test.OdataTests.d1.MoveNext() in C:\Payways\flexconnect\Tests\Nocore.FlexConnector.Application.Test\OdataTests.cs:line 45

This exception was originally thrown at this call stack:
  Microsoft.OData.ODataBatchUtils.ValidateReferenceUri(System.Uri, System.Collections.Generic.IEnumerable<string>, System.Uri)
  Microsoft.OData.ODataBatchWriter.BuildOperationRequestMessage(System.IO.Stream, string, System.Uri, string, string, System.Collections.Generic.IEnumerable<string>)
  Microsoft.OData.Json.ODataJsonBatchWriter.CreateOperationRequestMessageImplementation(string, System.Uri, string, Microsoft.OData.BatchPayloadUriOption, System.Collections.Generic.IEnumerable<string>)
  Microsoft.OData.ODataBatchWriter.CreateOperationRequestMessageInternal(string, System.Uri, string, Microsoft.OData.BatchPayloadUriOption, System.Collections.Generic.IEnumerable<string>)
  Microsoft.OData.ODataBatchWriter.CreateOperationRequestMessage(string, System.Uri, string, Microsoft.OData.BatchPayloadUriOption, System.Collections.Generic.IEnumerable<string>)
  Microsoft.OData.Client.ODataRequestMessageWrapper.CreateBatchPartRequestMessage(Microsoft.OData.ODataBatchWriter, Microsoft.OData.Client.BuildingRequestEventArgs, Microsoft.OData.Client.RequestInfo, string, bool)
  Microsoft.OData.Client.BatchSaveResult.CreateRequestMessage(string, System.Uri, Microsoft.OData.Client.HeaderCollection, Microsoft.OData.Client.HttpStack, Microsoft.OData.Client.Descriptor, string)
  Microsoft.OData.Client.BaseSaveResult.CreateRequest(Microsoft.OData.Client.EntityDescriptor)
  Microsoft.OData.Client.BatchSaveResult.GenerateBatchRequest()
  Microsoft.OData.Client.BatchSaveResult.BatchBeginRequest()

Additional detail

Might be because of the Guid as Id's?

DButoyez commented 1 month ago

Hello @MitchKuijpers,

I've done some preliminary investigations, and it appears the AddRelatedObject doesn't seem to support the dependsOnIDs feature, while the feature seems to have been implemented for the DeleteObject and UpdateObject

I'm still in the process of investigating some workarounds and conducting replications. I will revert back as soon as I have confirmed my observations.