diff --git a/src/Microsoft.OData.Client/DataServiceContext.cs b/src/Microsoft.OData.Client/DataServiceContext.cs index e614d0bb46..1ece0ba678 100644 --- a/src/Microsoft.OData.Client/DataServiceContext.cs +++ b/src/Microsoft.OData.Client/DataServiceContext.cs @@ -2634,7 +2634,8 @@ public virtual void AddRelatedObject(object source, string sourceProperty, objec var targetResource = new EntityDescriptor(this.model) { Entity = target, - State = EntityStates.Added + State = EntityStates.Added, + DependsOnIds = new List { sourceResource.ChangeOrder.ToString(CultureInfo.InvariantCulture) } }; targetResource.SetParentForInsert(sourceResource, sourceProperty); diff --git a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Batch/AsyncMethodTests.cs b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Batch/AsyncMethodTests.cs new file mode 100644 index 0000000000..ba986869af --- /dev/null +++ b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Batch/AsyncMethodTests.cs @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using Microsoft.AspNetCore.OData; +using Microsoft.AspNetCore.OData.Batch; +using Microsoft.AspNetCore.OData.Routing.Controllers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OData.Client.E2E.TestCommon; +using Microsoft.OData.Client.E2E.Tests.Batch.Server; +using Microsoft.OData.Client.E2E.Tests.Common.Server.EndToEnd; +using Microsoft.OData.Edm.Csdl; +using System.Xml; +using Xunit; + +namespace Microsoft.OData.Client.E2E.Tests.Batch +{ + public class AsyncMethodTests : EndToEndTestBase + { + private readonly Uri _baseUri; + private readonly Container _context; + + public class TestsStartup : TestStartupBase + { + public override void ConfigureServices(IServiceCollection services) + { + services.ConfigureControllers(typeof(BanksController), typeof(MetadataController)); + services.AddControllers().AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(null) + .AddRouteComponents("odata", CommonEndToEndEdmModel.GetEdmModel(), new DefaultODataBatchHandler())); + } + } + + public AsyncMethodTests(TestWebApplicationFactory fixture) + : base(fixture) + { + _baseUri = new Uri(Client.BaseAddress, "odata/"); + _context = new Container(_baseUri) + { + HttpClientFactory = HttpClientFactory + }; + } + + [Fact] + public async Task JsonBatchSequencingSingeChangeSetTest() + { + // Create new BankAccounts object + var bank = new Bank + { + Id = 45, + Name = "Test Bank", + Location = "KE", + BankAccounts = new List() + }; + + // Create new BankAccount object + var bankAccount = new BankAccount + { + Id = 890, + AccountNumber = "4567890", + BankId = bank.Id, + Bank = bank + }; + + // Establish the relationship between BankAccounts and BankAccount + bank.BankAccounts.Add(bankAccount); + + // Add the BankAccounts entity to the context + _context.AddObject("Banks", bank); + + // Add the related BankAccount entity + _context.AddRelatedObject(bank, "BankAccounts", bankAccount); + + // Save both entities in a single batch request using JSON + var response = await _context.SaveChangesAsync(SaveChangesOptions.BatchWithSingleChangeset | SaveChangesOptions.UseJsonBatch); + Assert.Equal(2, response.Count()); + + var bankResponse = response.First() as ChangeOperationResponse; + var bankAccountResponse = response.Last() as ChangeOperationResponse; + + Assert.NotNull(bankResponse); + Assert.NotNull(bankAccountResponse); + + Assert.Equal(201, bankResponse.StatusCode); + Assert.Equal(201, bankAccountResponse.StatusCode); + } + } + + class Container : DataServiceContext + { + public Container(Uri serviceRoot) : + base(serviceRoot, ODataProtocolVersion.V4) + { + Format.LoadServiceModel = () => CsdlReader.Parse(XmlReader.Create(new StringReader(@" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "))); + Format.UseJson(); + Banks = base.CreateQuery("Banks"); + BankAccounts = base.CreateQuery("BankAccounts"); + } + + public DataServiceQuery Banks { get; private set; } + public DataServiceQuery BankAccounts { get; private set; } + } +} + diff --git a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Batch/Server/BanksController.cs b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Batch/Server/BanksController.cs new file mode 100644 index 0000000000..62ef0350e2 --- /dev/null +++ b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Batch/Server/BanksController.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Formatter; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Routing.Controllers; +using Microsoft.OData.Client.E2E.Tests.Common.Server.EndToEnd; + +namespace Microsoft.OData.Client.E2E.Tests.Batch.Server +{ + public class BanksController : ODataController + { + private static CommonEndToEndDataSource _dataSource = CommonEndToEndDataSource.CreateInstance(); + + [EnableQuery] + [HttpGet("odata/Banks")] + public IActionResult Get() + { + var banks = _dataSource.Banks; + return Ok(banks); + } + + // POST: odata/Banks + [EnableQuery] + [HttpPost("odata/Banks")] + public IActionResult Post([FromBody] Bank bank) + { + if (bank == null) + { + return BadRequest(); + } + _dataSource.Banks.Add(bank); + return Created(bank); + } + + // POST: /odata/$1/BankAccounts + [HttpPost] + [Route("odata/Banks({id})/BankAccounts")] + public IActionResult PostBankAccount([FromODataUri] int id , [FromBody] BankAccount bankAccount) + { + if (bankAccount == null) + { + return BadRequest(); + } + + var bank = _dataSource.Banks.FirstOrDefault(b => b.Id == id); + if (bank == null) + { + return NotFound(); + } + + bankAccount.Bank = bank; + return Created(bankAccount); + } + } +} diff --git a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/Default/DefaultEdmModel.cs b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/Default/DefaultEdmModel.cs index aea5cd3642..87a6e805f2 100644 --- a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/Default/DefaultEdmModel.cs +++ b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/Default/DefaultEdmModel.cs @@ -5,6 +5,7 @@ // //------------------------------------------------------------------------------ +using Microsoft.OData.Client.E2E.Tests.Common.Server.EndToEnd; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; @@ -37,6 +38,8 @@ public static IEdmModel GetEdmModel() builder.EntitySet("StoredPIs"); builder.EntitySet("SubscriptionTemplates"); builder.Singleton("DefaultStoredPI"); + builder.EntitySet("Banks"); + builder.EntitySet("BankAccounts"); builder.EntityType() .Action("AddAccessRight") diff --git a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataModel.cs b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataModel.cs index f75e78dcb3..3b6a6c66e1 100644 --- a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataModel.cs +++ b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataModel.cs @@ -396,4 +396,22 @@ public class ComplexWithAllPrimitiveTypes public GeographyPoint? GeographyPoint { get; set; } public GeometryPoint? GeometryPoint { get; set; } } + + [Key("Id")] + public class Bank + { + public int Id { get; set; } + public string Name { get; set; } + public string Location { get; set; } + public ICollection BankAccounts { get; set; } + } + + [Key("Id")] + public class BankAccount + { + public int Id { get; set; } + public string AccountNumber { get; set; } + public int BankId { get; set; } + public Bank Bank { get; set; } + } } diff --git a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataSource.cs b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataSource.cs index 3e741bc64e..2a69e04706 100644 --- a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataSource.cs +++ b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndDataSource.cs @@ -70,6 +70,7 @@ private void InitializeData() PopulatePerson_PersonMetadata(); PopulateEmployee_Manager(); PopulateSpecialEmployee_Car(); + PopulateBank(); } private void ResetData() @@ -124,6 +125,31 @@ private void ResetData() public IList? Drivers { get; private set; } public IList? Licenses { get; private set; } public IList? PersonMetadata { get; private set; } + public IList? Banks { get; private set; } + public IList? BankAccounts { get; private set; } + + private void PopulateBank() + { + this.Banks = + [ + new (){ + Id = 300, + Name = "ICM", + Location = "KE", + BankAccounts = new List() + } + ]; + + } + + private void PopulateBankAccount() + { + this.AddBankAccountToBank(300, new BankAccount() + { + AccountNumber = "2002", + BankId = 300 + }); + } private void PopulateAllTypesSet() { @@ -8413,6 +8439,23 @@ private void AddLoginOrderReference(string username, params int[] orderIds) } } + private void AddBankAccountToBank(int bankId, params BankAccount[] bankAccounts) + { + var bank = this.Banks.FirstOrDefault(b => b.Id == bankId); + + if (bank != null) + { + foreach (var bankAccount in bankAccounts) + { + if (bankAccount != null) + { + bankAccount.Bank = bank; + bank.BankAccounts.Add(bankAccount); + } + } + } + } + private void AddMessageAttachmentReference(int messageId, params Guid[] attachmentIds) { var message = this.Messages.FirstOrDefault(m => m.MessageId == messageId); diff --git a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndEdmModel.cs b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndEdmModel.cs index 659dfb9b37..f17b813c59 100644 --- a/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndEdmModel.cs +++ b/test/EndToEndTests/Tests/Client/Microsoft.OData.Client.E2E.Tests/Common/Server/EndToEnd/CommonEndToEndEdmModel.cs @@ -39,6 +39,8 @@ public static IEdmModel GetEdmModel() builder.EntitySet("Licenses"); builder.EntitySet("MappedEntityTypes"); builder.EntitySet("Cars"); + builder.EntitySet("Banks"); + builder.EntitySet("BankAccounts"); builder.Action("RetrieveProduct") .Returns();