From e0fe97470bfbf3b6fc161be86fbe450d3aae360d Mon Sep 17 00:00:00 2001 From: Ilya Naidanov Date: Mon, 4 Mar 2024 14:31:56 +0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B1=D0=B8=D0=BB=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CRUD/BatchTest.cs | 515 +++++++++--------- 1 file changed, 260 insertions(+), 255 deletions(-) diff --git a/Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/BatchTest.cs b/Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/BatchTest.cs index c0de8995..8dc8960e 100644 --- a/Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/BatchTest.cs +++ b/Tests/NewPlatform.Flexberry.ORM.ODataService.Tests/CRUD/BatchTest.cs @@ -1,255 +1,260 @@ -namespace NewPlatform.Flexberry.ORM.ODataService.Tests.CRUD.Update -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - - using ICSSoft.STORMNET; - using ICSSoft.STORMNET.Business.LINQProvider; - using ICSSoft.STORMNET.KeyGen; - using NewPlatform.Flexberry.ORM.ODataService.Tests.Extensions; - using NewPlatform.Flexberry.ORM.ODataService.Tests.Helpers; - using Xunit; - using View = ICSSoft.STORMNET.View; - - /// - /// The class of tests for CRUD operations at Batch form. - /// There are extra batch tests at . - /// - public class BatchTest : BaseODataServiceIntegratedTest - { -#if NETCOREAPP - /// - /// Default constructor. - /// - /// Factory for application. - /// Output for debug information. - public BatchTest(CustomWebApplicationFactory factory, Xunit.Abstractions.ITestOutputHelper output) - : base(factory, output) - { - } -#endif - - /// - /// Test batch update of master-class with class at the same time. - /// It checks that dataobject cache is not crashed. - /// There are a master and object with link to master at batch request. Master is the first at the batch request. The link between object and master is not changed. - /// It is necessary that during batch processing master stay the same and is not overwriten. - /// - [Fact] - public void UpdateMasterAndClassTest() - { - ActODataService(args => - { - // Arrange. - string[] porodaPropertiesNames = - { - Information.ExtractPropertyPath<Порода>(x => x.__PrimaryKey), - Information.ExtractPropertyPath<Порода>(x => x.Название), - }; - string[] koshkaPropertiesNames = - { - Information.ExtractPropertyPath<Кошка>(x => x.__PrimaryKey), - Information.ExtractPropertyPath<Кошка>(x => x.Кличка), - }; - View porodaDynamicView = new View(new ViewAttribute("porodaDynamicView", porodaPropertiesNames), typeof(Порода)); - View koshkaDynamicView = new View(new ViewAttribute("koshkaDynamicView", koshkaPropertiesNames), typeof(Кошка)); - - const string InitialName = "Initial"; - const string OtherName = "Other"; - Порода poroda = new Порода() { Название = InitialName }; - Кошка koshka = new Кошка() { Кличка = InitialName, Порода = poroda}; - args.DataService.UpdateObject(koshka); - - Порода poroda1 = args.DataService.Query<Порода>(porodaDynamicView).FirstOrDefault(x => x.__PrimaryKey == poroda.__PrimaryKey); - Кошка koshka1 = args.DataService.Query<Кошка>(koshkaDynamicView).FirstOrDefault(x => x.__PrimaryKey == koshka.__PrimaryKey); - Assert.NotNull(poroda1); - Assert.NotNull(koshka1); - - poroda.Название = OtherName; - koshka.Кличка = OtherName; - - string requestJsonDatakoshka = koshka.ToJson(koshkaDynamicView, args.Token.Model); - DataObjectDictionary objJsonKoshka = DataObjectDictionary.Parse(requestJsonDatakoshka, koshkaDynamicView, args.Token.Model); - - objJsonKoshka.Add( - $"{nameof(Кошка.Порода)}@odata.bind", - string.Format( - "{0}({1})", - args.Token.Model.GetEdmEntitySet(typeof(Порода)).Name, - ((KeyGuid)poroda.__PrimaryKey).Guid.ToString("D"))); - - requestJsonDatakoshka = objJsonKoshka.Serialize(); - - const string baseUrl = "http://localhost/odata"; - string[] changesets = new[] // Важно, чтобы сначала шёл мастер, потом объект, имеющий на него ссылку. - { - - CreateChangeset( - $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(Порода)).Name}", - poroda.ToJson(porodaDynamicView, args.Token.Model), - poroda), - CreateChangeset( - $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(Кошка)).Name}", - requestJsonDatakoshka, - koshka), - }; - - // Act. - HttpRequestMessage batchRequest = CreateBatchRequest(baseUrl, changesets); - using (HttpResponseMessage response = args.HttpClient.SendAsync(batchRequest).Result) - { - // Assert. - CheckODataBatchResponseStatusCode(response, new HttpStatusCode[] { HttpStatusCode.OK, HttpStatusCode.OK }); - Порода poroda2 = args.DataService.Query<Порода>(porodaDynamicView).FirstOrDefault(x => x.__PrimaryKey == poroda.__PrimaryKey); - Кошка koshka2 = args.DataService.Query<Кошка>(koshkaDynamicView).FirstOrDefault(x => x.__PrimaryKey == koshka.__PrimaryKey); - Assert.NotNull(poroda2); - Assert.NotNull(koshka2); - Assert.Equal(OtherName, poroda2.Название); - Assert.Equal(OtherName, koshka2.Кличка); - } - }); - } - - /// - /// Test batch update with inheritance. - /// It checks that dataobject cache is not crashed. - /// There are classes A, its detail B, that has descendant C. During class A loading its details are loaded too, but details are loaded by View of class B, while details are of class C. - /// Thus there are objects of type C at the cache while they are loaded by properties of class B only. That's why the state of details is LightLoaded. - /// It is necessary to post-load only propertues that are not loaded before (loaded properties can be changed). - /// - [Fact] - public void UpdateWithInheritanceAndDetailsTest() - { - ActODataService(args => - { - // Arrange. - const string InitialName = "Initial"; - const string OtherName = "Other"; - TestConfiguration testConfiguration = new TestConfiguration() { Name = InitialName }; - FirstLevel first = new FirstLevel() { Name = InitialName, TestConfiguration = testConfiguration }; - TestClass second1 = new TestClass { Name = InitialName, FirstLevel = first }; - TestAssociation second2 = new TestAssociation { Name = InitialName, FirstLevel = first, SecondLevel1 = second1 }; - ThirdLevel third = new ThirdLevel { Name = InitialName, TestClass = second1 }; - DataObject[] updateObjects = new DataObject[] { testConfiguration, first, second1, second2, third }; - args.DataService.UpdateObjects(ref updateObjects); - - second2.Name = OtherName; // Изменение значения детейла одного типа, который имеет мастеровую ссылку на детейл второго типа (второй тип имеет детейл собственный). - ThirdLevel third2 = new ThirdLevel { Name = OtherName, TestClass = second1 }; // Добавление детейлов в детейл второго типа. - - string[] firstPropertiesNames = - { - Information.ExtractPropertyPath(x => x.__PrimaryKey), - Information.ExtractPropertyPath(x => x.Name), - }; - View firstLevelDynamicView = new View(new ViewAttribute("firstLevelDynamicView", firstPropertiesNames), typeof(FirstLevel)); - - string[] second1PropertiesNames = - { - Information.ExtractPropertyPath(x => x.__PrimaryKey), - Information.ExtractPropertyPath(x => x.Name), - }; - View second1DynamicView = new View(new ViewAttribute("second1DynamicView", second1PropertiesNames), typeof(TestClass)); - - string[] second2PropertiesNames = - { - Information.ExtractPropertyPath(x => x.__PrimaryKey), - Information.ExtractPropertyPath(x => x.Name), - }; - View second2DynamicView = new View(new ViewAttribute("second2DynamicView", second2PropertiesNames), typeof(TestAssociation)); - - string[] thirdPropertiesNames = - { - Information.ExtractPropertyPath(x => x.__PrimaryKey), - Information.ExtractPropertyPath(x => x.Name), - }; - View thirdLevelDynamicView = new View(new ViewAttribute("thirdDynamicView", thirdPropertiesNames), typeof(ThirdLevel)); - - // Операция изменения детейла второго типа (он попадает в батч-запрос как агрегатор к добавляемому детейлу второго уровня). - string requestJsonDataSecond1 = second1.ToJson(second1DynamicView, args.Token.Model); - DataObjectDictionary objJsonSecond1 = DataObjectDictionary.Parse(requestJsonDataSecond1, second1DynamicView, args.Token.Model); - objJsonSecond1.Add( // Добавляется ссылка на агрегатор. - $"{nameof(TestClass.FirstLevel)}@odata.bind", - string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(FirstLevel)).Name, ((KeyGuid)first.__PrimaryKey).Guid.ToString("D"))); - requestJsonDataSecond1 = objJsonSecond1.Serialize(); - - // Операция вставки детейла второго уровня. - string requestJsonDataThird2 = third2.ToJson(thirdLevelDynamicView, args.Token.Model); - DataObjectDictionary objJsonThird2 = DataObjectDictionary.Parse(requestJsonDataThird2, thirdLevelDynamicView, args.Token.Model); - objJsonThird2.Add( // Добавляется ссылка на агрегатор. - $"{nameof(ThirdLevel.TestClass)}@odata.bind", - string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(TestClass)).Name, ((KeyGuid)second1.__PrimaryKey).Guid.ToString("D"))); - requestJsonDataThird2 = objJsonThird2.Serialize(); - - // Операция изменения детейла первого типа. - string requestJsonDataSecond2 = second2.ToJson(second2DynamicView, args.Token.Model); - DataObjectDictionary objJsonSecond2 = DataObjectDictionary.Parse(requestJsonDataSecond2, second2DynamicView, args.Token.Model); - objJsonSecond2.Add( // Добавляется ссылка на агрегатор. - $"{nameof(TestAssociation.FirstLevel)}@odata.bind", - string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(FirstLevel)).Name, ((KeyGuid)first.__PrimaryKey).Guid.ToString("D"))); - objJsonSecond2.Add( // Добавляется ссылка мастеровая на другой детейл. - $"{nameof(TestAssociation.SecondLevel1)}@odata.bind", - string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(TestClass)).Name, ((KeyGuid)second1.__PrimaryKey).Guid.ToString("D"))); - requestJsonDataSecond2 = objJsonSecond2.Serialize(); - - const string baseUrl = "http://localhost/odata"; - string[] changesets = new[] - { - - CreateChangeset( - $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(TestClass)).Name}", - requestJsonDataSecond1, - second1), - CreateChangeset( - $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(ThirdLevel)).Name}", - requestJsonDataThird2, - third2), - CreateChangeset( - $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(TestAssociation)).Name}", - requestJsonDataSecond2, - second2), - }; - HttpRequestMessage batchRequest = CreateBatchRequest(baseUrl, changesets); - - // Код для удобства отлавливания исключений. - args.Token.Events.CallbackAfterInternalServerError = (Exception exception, ref HttpStatusCode code) => - { - Exception currentException = exception; - - while (currentException != null) - { - currentException = currentException.InnerException; - } - - return exception; - }; - - // Act. - using (HttpResponseMessage response = args.HttpClient.SendAsync(batchRequest).Result) - { - // Assert. - CheckODataBatchResponseStatusCode(response, new HttpStatusCode[] { HttpStatusCode.OK, HttpStatusCode.Created, HttpStatusCode.OK }); - - string[] thirdPropertiesNames2 = - { - Information.ExtractPropertyPath(x => x.__PrimaryKey), - Information.ExtractPropertyPath(x => x.Name), - Information.ExtractPropertyPath(x => x.TestClass), - }; - View thirdLevelDynamicView2 = new View(new ViewAttribute("thirdDynamicView2", thirdPropertiesNames2), typeof(ThirdLevel)); - List thirdLevelList = args.DataService.Query(thirdLevelDynamicView2).Where(x => x.TestClass.__PrimaryKey == second1.__PrimaryKey).ToList(); - Assert.NotNull(thirdLevelList); - Assert.True(thirdLevelList.Any()); - Assert.Equal(2, thirdLevelList.Count); - - TestAssociation checkAssociation = args.DataService.Query(second2DynamicView).FirstOrDefault(x => x.__PrimaryKey == second2.__PrimaryKey); - Assert.NotNull(checkAssociation); - Assert.Equal(OtherName, checkAssociation.Name); - } - }); - } - } -} - +namespace NewPlatform.Flexberry.ORM.ODataService.Tests.CRUD.Update +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Net.Http; + + using ICSSoft.STORMNET; + using ICSSoft.STORMNET.Business.LINQProvider; + using ICSSoft.STORMNET.KeyGen; + using NewPlatform.Flexberry.ORM.ODataService.Tests.Extensions; + using NewPlatform.Flexberry.ORM.ODataService.Tests.Helpers; + using Xunit; + using View = ICSSoft.STORMNET.View; + + /// + /// The class of tests for CRUD operations at Batch form. + /// There are extra batch tests at . + /// +#if NETFRAMEWORK + public class BatchTest : BaseODataServiceIntegratedTest +#endif +#if NETCOREAPP + public class BatchTest : BaseODataServiceIntegratedTest +#endif + { +#if NETCOREAPP + /// + /// Default constructor. + /// + /// Factory for application. + /// Output for debug information. + public BatchTest(CustomWebApplicationFactory factory, Xunit.Abstractions.ITestOutputHelper output) + : base(factory, output) + { + } +#endif + + /// + /// Test batch update of master-class with class at the same time. + /// It checks that dataobject cache is not crashed. + /// There are a master and object with link to master at batch request. Master is the first at the batch request. The link between object and master is not changed. + /// It is necessary that during batch processing master stay the same and is not overwriten. + /// + [Fact] + public void UpdateMasterAndClassTest() + { + ActODataService(args => + { + // Arrange. + string[] porodaPropertiesNames = + { + Information.ExtractPropertyPath<Порода>(x => x.__PrimaryKey), + Information.ExtractPropertyPath<Порода>(x => x.Название), + }; + string[] koshkaPropertiesNames = + { + Information.ExtractPropertyPath<Кошка>(x => x.__PrimaryKey), + Information.ExtractPropertyPath<Кошка>(x => x.Кличка), + }; + View porodaDynamicView = new View(new ViewAttribute("porodaDynamicView", porodaPropertiesNames), typeof(Порода)); + View koshkaDynamicView = new View(new ViewAttribute("koshkaDynamicView", koshkaPropertiesNames), typeof(Кошка)); + + const string InitialName = "Initial"; + const string OtherName = "Other"; + Порода poroda = new Порода() { Название = InitialName }; + Кошка koshka = new Кошка() { Кличка = InitialName, Порода = poroda}; + args.DataService.UpdateObject(koshka); + + Порода poroda1 = args.DataService.Query<Порода>(porodaDynamicView).FirstOrDefault(x => x.__PrimaryKey == poroda.__PrimaryKey); + Кошка koshka1 = args.DataService.Query<Кошка>(koshkaDynamicView).FirstOrDefault(x => x.__PrimaryKey == koshka.__PrimaryKey); + Assert.NotNull(poroda1); + Assert.NotNull(koshka1); + + poroda.Название = OtherName; + koshka.Кличка = OtherName; + + string requestJsonDatakoshka = koshka.ToJson(koshkaDynamicView, args.Token.Model); + DataObjectDictionary objJsonKoshka = DataObjectDictionary.Parse(requestJsonDatakoshka, koshkaDynamicView, args.Token.Model); + + objJsonKoshka.Add( + $"{nameof(Кошка.Порода)}@odata.bind", + string.Format( + "{0}({1})", + args.Token.Model.GetEdmEntitySet(typeof(Порода)).Name, + ((KeyGuid)poroda.__PrimaryKey).Guid.ToString("D"))); + + requestJsonDatakoshka = objJsonKoshka.Serialize(); + + const string baseUrl = "http://localhost/odata"; + string[] changesets = new[] // Важно, чтобы сначала шёл мастер, потом объект, имеющий на него ссылку. + { + + CreateChangeset( + $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(Порода)).Name}", + poroda.ToJson(porodaDynamicView, args.Token.Model), + poroda), + CreateChangeset( + $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(Кошка)).Name}", + requestJsonDatakoshka, + koshka), + }; + + // Act. + HttpRequestMessage batchRequest = CreateBatchRequest(baseUrl, changesets); + using (HttpResponseMessage response = args.HttpClient.SendAsync(batchRequest).Result) + { + // Assert. + CheckODataBatchResponseStatusCode(response, new HttpStatusCode[] { HttpStatusCode.OK, HttpStatusCode.OK }); + Порода poroda2 = args.DataService.Query<Порода>(porodaDynamicView).FirstOrDefault(x => x.__PrimaryKey == poroda.__PrimaryKey); + Кошка koshka2 = args.DataService.Query<Кошка>(koshkaDynamicView).FirstOrDefault(x => x.__PrimaryKey == koshka.__PrimaryKey); + Assert.NotNull(poroda2); + Assert.NotNull(koshka2); + Assert.Equal(OtherName, poroda2.Название); + Assert.Equal(OtherName, koshka2.Кличка); + } + }); + } + + /// + /// Test batch update with inheritance. + /// It checks that dataobject cache is not crashed. + /// There are classes A, its detail B, that has descendant C. During class A loading its details are loaded too, but details are loaded by View of class B, while details are of class C. + /// Thus there are objects of type C at the cache while they are loaded by properties of class B only. That's why the state of details is LightLoaded. + /// It is necessary to post-load only propertues that are not loaded before (loaded properties can be changed). + /// + [Fact] + public void UpdateWithInheritanceAndDetailsTest() + { + ActODataService(args => + { + // Arrange. + const string InitialName = "Initial"; + const string OtherName = "Other"; + TestConfiguration testConfiguration = new TestConfiguration() { Name = InitialName }; + FirstLevel first = new FirstLevel() { Name = InitialName, TestConfiguration = testConfiguration }; + TestClass second1 = new TestClass { Name = InitialName, FirstLevel = first }; + TestAssociation second2 = new TestAssociation { Name = InitialName, FirstLevel = first, SecondLevel1 = second1 }; + ThirdLevel third = new ThirdLevel { Name = InitialName, TestClass = second1 }; + DataObject[] updateObjects = new DataObject[] { testConfiguration, first, second1, second2, third }; + args.DataService.UpdateObjects(ref updateObjects); + + second2.Name = OtherName; // Изменение значения детейла одного типа, который имеет мастеровую ссылку на детейл второго типа (второй тип имеет детейл собственный). + ThirdLevel third2 = new ThirdLevel { Name = OtherName, TestClass = second1 }; // Добавление детейлов в детейл второго типа. + + string[] firstPropertiesNames = + { + Information.ExtractPropertyPath(x => x.__PrimaryKey), + Information.ExtractPropertyPath(x => x.Name), + }; + View firstLevelDynamicView = new View(new ViewAttribute("firstLevelDynamicView", firstPropertiesNames), typeof(FirstLevel)); + + string[] second1PropertiesNames = + { + Information.ExtractPropertyPath(x => x.__PrimaryKey), + Information.ExtractPropertyPath(x => x.Name), + }; + View second1DynamicView = new View(new ViewAttribute("second1DynamicView", second1PropertiesNames), typeof(TestClass)); + + string[] second2PropertiesNames = + { + Information.ExtractPropertyPath(x => x.__PrimaryKey), + Information.ExtractPropertyPath(x => x.Name), + }; + View second2DynamicView = new View(new ViewAttribute("second2DynamicView", second2PropertiesNames), typeof(TestAssociation)); + + string[] thirdPropertiesNames = + { + Information.ExtractPropertyPath(x => x.__PrimaryKey), + Information.ExtractPropertyPath(x => x.Name), + }; + View thirdLevelDynamicView = new View(new ViewAttribute("thirdDynamicView", thirdPropertiesNames), typeof(ThirdLevel)); + + // Операция изменения детейла второго типа (он попадает в батч-запрос как агрегатор к добавляемому детейлу второго уровня). + string requestJsonDataSecond1 = second1.ToJson(second1DynamicView, args.Token.Model); + DataObjectDictionary objJsonSecond1 = DataObjectDictionary.Parse(requestJsonDataSecond1, second1DynamicView, args.Token.Model); + objJsonSecond1.Add( // Добавляется ссылка на агрегатор. + $"{nameof(TestClass.FirstLevel)}@odata.bind", + string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(FirstLevel)).Name, ((KeyGuid)first.__PrimaryKey).Guid.ToString("D"))); + requestJsonDataSecond1 = objJsonSecond1.Serialize(); + + // Операция вставки детейла второго уровня. + string requestJsonDataThird2 = third2.ToJson(thirdLevelDynamicView, args.Token.Model); + DataObjectDictionary objJsonThird2 = DataObjectDictionary.Parse(requestJsonDataThird2, thirdLevelDynamicView, args.Token.Model); + objJsonThird2.Add( // Добавляется ссылка на агрегатор. + $"{nameof(ThirdLevel.TestClass)}@odata.bind", + string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(TestClass)).Name, ((KeyGuid)second1.__PrimaryKey).Guid.ToString("D"))); + requestJsonDataThird2 = objJsonThird2.Serialize(); + + // Операция изменения детейла первого типа. + string requestJsonDataSecond2 = second2.ToJson(second2DynamicView, args.Token.Model); + DataObjectDictionary objJsonSecond2 = DataObjectDictionary.Parse(requestJsonDataSecond2, second2DynamicView, args.Token.Model); + objJsonSecond2.Add( // Добавляется ссылка на агрегатор. + $"{nameof(TestAssociation.FirstLevel)}@odata.bind", + string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(FirstLevel)).Name, ((KeyGuid)first.__PrimaryKey).Guid.ToString("D"))); + objJsonSecond2.Add( // Добавляется ссылка мастеровая на другой детейл. + $"{nameof(TestAssociation.SecondLevel1)}@odata.bind", + string.Format("{0}({1})", args.Token.Model.GetEdmEntitySet(typeof(TestClass)).Name, ((KeyGuid)second1.__PrimaryKey).Guid.ToString("D"))); + requestJsonDataSecond2 = objJsonSecond2.Serialize(); + + const string baseUrl = "http://localhost/odata"; + string[] changesets = new[] + { + + CreateChangeset( + $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(TestClass)).Name}", + requestJsonDataSecond1, + second1), + CreateChangeset( + $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(ThirdLevel)).Name}", + requestJsonDataThird2, + third2), + CreateChangeset( + $"{baseUrl}/{args.Token.Model.GetEdmEntitySet(typeof(TestAssociation)).Name}", + requestJsonDataSecond2, + second2), + }; + HttpRequestMessage batchRequest = CreateBatchRequest(baseUrl, changesets); + + // Код для удобства отлавливания исключений. + args.Token.Events.CallbackAfterInternalServerError = (Exception exception, ref HttpStatusCode code) => + { + Exception currentException = exception; + + while (currentException != null) + { + currentException = currentException.InnerException; + } + + return exception; + }; + + // Act. + using (HttpResponseMessage response = args.HttpClient.SendAsync(batchRequest).Result) + { + // Assert. + CheckODataBatchResponseStatusCode(response, new HttpStatusCode[] { HttpStatusCode.OK, HttpStatusCode.Created, HttpStatusCode.OK }); + + string[] thirdPropertiesNames2 = + { + Information.ExtractPropertyPath(x => x.__PrimaryKey), + Information.ExtractPropertyPath(x => x.Name), + Information.ExtractPropertyPath(x => x.TestClass), + }; + View thirdLevelDynamicView2 = new View(new ViewAttribute("thirdDynamicView2", thirdPropertiesNames2), typeof(ThirdLevel)); + List thirdLevelList = args.DataService.Query(thirdLevelDynamicView2).Where(x => x.TestClass.__PrimaryKey == second1.__PrimaryKey).ToList(); + Assert.NotNull(thirdLevelList); + Assert.True(thirdLevelList.Any()); + Assert.Equal(2, thirdLevelList.Count); + + TestAssociation checkAssociation = args.DataService.Query(second2DynamicView).FirstOrDefault(x => x.__PrimaryKey == second2.__PrimaryKey); + Assert.NotNull(checkAssociation); + Assert.Equal(OtherName, checkAssociation.Name); + } + }); + } + } +} +