diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs index 9783fd9052..6f7649077d 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs @@ -202,12 +202,13 @@ private void WriteDeltaResource(object graph, ODataWriter writer, ODataSerialize { writer.WriteStart(resource); WriteDeltaComplexProperties(selectExpandNode, resourceContext, writer); + WriteDeltaNavigationProperties(selectExpandNode, resourceContext, writer); //TODO: Need to add support to write Navigation Links, etc. using Delta Writer //https://github.com/OData/odata.net/issues/155 //CLEANUP: merge delta logic with regular logic; requires common base between ODataWriter and ODataDeltaWriter //WriteDynamicComplexProperties(resourceContext, writer); //WriteNavigationLinks(selectExpandNode.SelectedNavigationProperties, resourceContext, writer); - //WriteExpandedNavigationProperties(selectExpandNode.ExpandedNavigationProperties, resourceContext, writer); + //WriteExpandedNavigationProperties(selectExpandNode, resourceContext, writer); writer.WriteEnd(); } @@ -226,6 +227,7 @@ private async Task WriteDeltaResourceAsync(object graph, ODataWriter writer, ODa { await writer.WriteStartAsync(resource); await WriteDeltaComplexPropertiesAsync(selectExpandNode, resourceContext, writer); + await WriteDeltaNavigationPropertiesAsync(selectExpandNode, resourceContext, writer); //TODO: Need to add support to write Navigation Links, etc. using Delta Writer //https://github.com/OData/odata.net/issues/155 //CLEANUP: merge delta logic with regular logic; requires common base between ODataWriter and ODataDeltaWriter @@ -275,6 +277,48 @@ internal void WriteDeltaComplexProperties(SelectExpandNode selectExpandNode, } } + internal void WriteDeltaNavigationProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) + { + Contract.Assert(resourceContext != null); + Contract.Assert(writer != null); + + IEnumerable> navigationProperties = GetNavigationPropertiesToWrite(selectExpandNode, resourceContext); + + foreach (KeyValuePair navigationProperty in navigationProperties) + { + ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo + { + IsCollection = navigationProperty.Key.Type.IsCollection(), + Name = navigationProperty.Key.Name + }; + + writer.WriteStart(nestedResourceInfo); + WriteDeltaComplexAndExpandedNavigationProperty(navigationProperty.Key, null, resourceContext, writer, navigationProperty.Value); + writer.WriteEnd(); + } + } + + internal async Task WriteDeltaNavigationPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) + { + Contract.Assert(resourceContext != null); + Contract.Assert(writer != null); + + IEnumerable> navigationProperties = GetNavigationPropertiesToWrite(selectExpandNode, resourceContext); + + foreach (KeyValuePair navigationProperty in navigationProperties) + { + ODataNestedResourceInfo nestedResourceInfo = new ODataNestedResourceInfo + { + IsCollection = navigationProperty.Key.Type.IsCollection(), + Name = navigationProperty.Key.Name + }; + + await writer.WriteStartAsync(nestedResourceInfo); + await WriteDeltaComplexAndExpandedNavigationPropertyAsync(navigationProperty.Key, null, resourceContext, writer, navigationProperty.Value); + await writer.WriteEndAsync(); + } + } + private async Task WriteDeltaComplexPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) { @@ -298,7 +342,7 @@ private async Task WriteDeltaComplexPropertiesAsync(SelectExpandNode selectExpan } private void WriteDeltaComplexAndExpandedNavigationProperty(IEdmProperty edmProperty, SelectExpandClause selectExpandClause, - ResourceContext resourceContext, ODataWriter writer) + ResourceContext resourceContext, ODataWriter writer, Type type = null) { Contract.Assert(edmProperty != null); Contract.Assert(resourceContext != null); @@ -331,6 +375,7 @@ private void WriteDeltaComplexAndExpandedNavigationProperty(IEdmProperty edmProp { // create the serializer context for the complex and expanded item. ODataSerializerContext nestedWriteContext = new ODataSerializerContext(resourceContext, selectExpandClause, edmProperty); + nestedWriteContext.Type = type; // write object. @@ -355,7 +400,7 @@ private void WriteDeltaComplexAndExpandedNavigationProperty(IEdmProperty edmProp } private async Task WriteDeltaComplexAndExpandedNavigationPropertyAsync(IEdmProperty edmProperty, SelectExpandClause selectExpandClause, - ResourceContext resourceContext, ODataWriter writer) + ResourceContext resourceContext, ODataWriter writer, Type type = null) { Contract.Assert(edmProperty != null); Contract.Assert(resourceContext != null); @@ -388,6 +433,7 @@ await writer.WriteStartAsync(new ODataResourceSet { // create the serializer context for the complex and expanded item. ODataSerializerContext nestedWriteContext = new ODataSerializerContext(resourceContext, selectExpandClause, edmProperty); + nestedWriteContext.Type = type; // write object. @@ -1351,6 +1397,43 @@ private IEnumerable> GetPro } } + private IEnumerable> GetNavigationPropertiesToWrite(SelectExpandNode selectExpandNode, ResourceContext resourceContext) + { + ISet navigationProperties = selectExpandNode.SelectedNavigationProperties; + + if (navigationProperties != null) + { + IEnumerable changedProperties = null; + + if (null != resourceContext.ResourceInstance && resourceContext.ResourceInstance is IDelta deltaObject) + { + changedProperties = deltaObject.GetChangedPropertyNames(); + dynamic delta = deltaObject; + + foreach (IEdmNavigationProperty navigationProperty in navigationProperties) + { + Object obj = null; + if (changedProperties == null || changedProperties.Contains(navigationProperty.Name) && delta.DeltaNestedResources.TryGetValue(navigationProperty.Name, out obj)) + { + yield return new KeyValuePair(navigationProperty, obj.GetType()); + } + } + } + else if(null != resourceContext.EdmObject && resourceContext.EdmObject is IDelta changedObject) + { + changedProperties = changedObject.GetChangedPropertyNames(); + + foreach (IEdmNavigationProperty navigationProperty in navigationProperties) + { + if (changedProperties == null || changedProperties.Contains(navigationProperty.Name)) + { + yield return new KeyValuePair(navigationProperty, typeof(IEdmChangedObject)); + } + } + } + } + } + private void WriteExpandedNavigationProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) { Contract.Assert(resourceContext != null); diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs index 914ff6054a..bde531d415 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataSerializerContext.cs @@ -172,7 +172,7 @@ internal bool IsUntyped get { if (_isUntyped == null) { - _isUntyped = typeof(IEdmObject).IsAssignableFrom(Type); + _isUntyped = typeof(IEdmObject).IsAssignableFrom(Type) || typeof(EdmChangedObjectCollection).IsAssignableFrom(Type); } return _isUntyped.Value; diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs index 4c49ce99e9..e80614c931 100644 --- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs +++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs @@ -198,6 +198,7 @@ public ITestActionResult Get() return Ok(Employees.AsQueryable()); } + [EnableQuery] public ITestActionResult Get(int key) { var emp = Employees.SingleOrDefault(e => e.ID == key); @@ -235,6 +236,7 @@ public ITestActionResult GetUnTypedFriends(int key) [ODataRoute("Employees")] [HttpPatch] + [EnableQuery] public ITestActionResult PatchEmployees([FromBody] DeltaSet coll) { InitEmployees(); @@ -358,6 +360,7 @@ public ITestActionResult PatchUnTypedEmployees([FromBody] EdmChangedObjectCollec [ODataRoute("Employees({key})")] + [EnableQuery] public ITestActionResult Patch(int key, [FromBody] Delta delta) { InitEmployees(); diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs index 0a636d9096..e1bae87c56 100644 --- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs +++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs @@ -10,6 +10,7 @@ namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert { + [AutoExpand] public class Employee { [Key] @@ -19,7 +20,6 @@ public class Employee public Gender Gender { get; set; } public AccessLevel AccessLevel { get; set; } - [AutoExpand] public List Friends { get; set; } public List NewFriends { get; set; } @@ -67,9 +67,9 @@ public class Friend { [Key] public int Id { get; set; } - [Required] + public string Name { get; set; } - [Range(10, 20)] + public int Age { get; set; } public List Orders { get; set; } diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs index 6127c9a585..672b8a569d 100644 --- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs +++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs @@ -142,15 +142,20 @@ public async Task PatchEmployee_WithUpdates_Friends() }"; var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); + var expected = "$delta\",\"value\":[{\"Id\":1,\"Name\":\"Friend1\",\"Age\":0}," + + "{\"Id\":2,\"Name\":\"Friend2\",\"Age\":0}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected, json.ToString()); } //Assert @@ -180,14 +185,19 @@ public async Task PatchEmployee_WithDeletes_Friends() }"; var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; + var expected = "$delta\",\"value\":[{\"@removed\":{\"reason\":\"changed\"}," + + "\"@id\":\""+this.BaseAddress+"/convention/Friends(1)\",\"Id\":1,\"Name\":null,\"Age\":0},{\"Id\":2,\"Name\":\"Friend2\",\"Age\":0}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected.ToLower(), json.ToString().ToLower()); } //Assert @@ -218,14 +228,19 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedTypes() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; + var expected = "$delta\",\"value\":[{\"@removed\":{\"reason\":\"changed\"}," + + "\"@id\":\""+this.BaseAddress+"/convention/Friends(1)\",\"Id\":1,\"Name\":null,\"Age\":0},{\"Id\":2,\"Name\":\"Friend2\",\"Age\":0}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected.ToLower(), json.ToString().ToLower()); } //Assert @@ -256,15 +271,20 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedDeletes() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; //Act & Assert + var expected = "$delta\",\"value\":[{\"@removed\":{\"reason\":\"changed\"}," + + "\"@id\":\""+ this.BaseAddress +"/convention/Friends(1)\",\"Id\":1,\"Name\":null,\"Age\":0},{\"Id\":2,\"Name\":\"Friend2\",\"Age\":0}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected.ToLower(), json.ToString().ToLower()); } @@ -294,7 +314,7 @@ public async Task PatchEmployee_WithAdds_Friends_WithAnnotations() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); - + requestForPost.Headers.Add("OData-Version", "4.01"); requestForPost.Content = new StringContent(content); requestForPost.Content.Headers.ContentType= MediaTypeWithQualityHeaderValue.Parse("application/json"); @@ -303,11 +323,13 @@ public async Task PatchEmployee_WithAdds_Friends_WithAnnotations() Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); + var expected = "$delta\",\"value\":[{\"@NS.Test\":1,\"Id\":3,\"Name\":null,\"Age\":35}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); - json.ToString().Contains("$deletedEntity"); + Assert.Contains(expected, json.ToString()); } } @@ -324,16 +346,19 @@ public async Task PatchEmployee_WithFailedAdds_Friends() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); + var expected = "$delta\",\"value\":[{\"@NS.Test\":1,\"Id\":3,\"Name\":null,\"Age\":3}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); - json.ToString().Contains("$deletedEntity"); + Assert.Contains(expected, json.ToString()); } } @@ -349,16 +374,21 @@ public async Task PatchEmployee_WithFailedDeletes_Friends() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); + var expected = "$delta\",\"value\":[{\"@NS.Test\":1,\"@Core.DataModificationException\":" + + "{\"@type\":\"#Org.OData.Core.V1.DataModificationExceptionType\"},\"Id\":2,\"Name\":null,\"Age\":15}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Contains("$delta", json); + Assert.Contains(expected, json.ToString()); } } @@ -376,11 +406,16 @@ public async Task PatchEmployee_WithFailedOperation_WithAnnotations() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); + var expected = "/convention/$metadata#NewFriends/$delta\",\"value\":[{\"@NS.Test2\":\"testing\",\"@Core.ContentID\":3," + + "\"@Core.DataModificationException\":{\"@type\":\"#Org.OData.Core.V1.DataModificationExceptionType\"},\"Id\":2,\"Name\":null,\"Age\":15}]}"; + + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; @@ -389,6 +424,7 @@ public async Task PatchEmployee_WithFailedOperation_WithAnnotations() Assert.Contains("$delta",str); Assert.Contains("NS.Test2", str); Assert.Contains("Core.DataModificationException", str); + Assert.Contains(expected, str); } } @@ -416,16 +452,22 @@ public async Task PatchUntypedEmployee_WithAdds_Friends_Untyped() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); + var expected = "/convention/$metadata#UnTypedEmployees/$delta\",\"value\":[{\"ID\":1,\"Name\":\"Employee1\",\"UnTypedFriends@delta\":" + + "[{\"Id\":1,\"Name\":\"Friend1\",\"Age\":0},{\"Id\":2,\"Name\":\"Friend2\",\"Age\":0}]},{\"ID\":2,\"Name\":\"Employee2\",\"UnTypedFriends@delta\":" + + "[{\"Id\":3,\"Name\":\"Friend3\",\"Age\":0},{\"Id\":4,\"Name\":\"Friend4\",\"Age\":0}]}]}"; + + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); - json.ToString().Contains("$deletedEntity"); + Assert.Contains(expected, json.ToString()); } } @@ -589,14 +631,21 @@ public async Task PatchEmployee_WithUpdates_Employees() }"; var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; - + + var expected = "\"value\":[{\"ID\":1,\"Name\":\"Employee1\",\"SkillSet\":[],\"Gender\":\"0\",\"AccessLevel\":\"0\",\"FavoriteSports\":null," + + "\"Friends@delta\":[{\"Id\":1,\"Name\":\"Friend1\",\"Age\":0,\"Orders@delta\":[{\"Id\":1,\"Price\":10},{\"Id\":2,\"Price\":20}]}," + + "{\"Id\":2,\"Name\":\"Friend2\",\"Age\":0}]},{\"ID\":2,\"Name\":\"Employee2\",\"SkillSet\":[],\"Gender\":\"0\",\"AccessLevel\":\"0\",\"FavoriteSports\":null," + + "\"Friends@delta\":[{\"Id\":3,\"Name\":\"Friend3\",\"Age\":0,\"Orders@delta\":[{\"Id\":3,\"Price\":30},{\"Id\":4,\"Price\":40}]},{\"Id\":4,\"Name\":\"Friend4\",\"Age\":0}]}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected, json.ToString()); } //Assert @@ -611,7 +660,7 @@ public async Task PatchEmployee_WithUpdates_Employees() Assert.Contains("Friend2", json.ToString()); } - requestUri = this.BaseAddress + "/convention/Employees(2)/Friends"; + requestUri = this.BaseAddress + "/convention/Employees(2)"; using (HttpResponseMessage response = await this.Client.GetAsync(requestUri)) { response.EnsureSuccessStatusCode(); @@ -637,14 +686,19 @@ public async Task PatchEmployee_WithDelete() }"; var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); - + StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; + var expected = "/convention/$metadata#Employees(Friends(),NewFriends(),UnTypedFriends())/$entity\",\"ID\":1,\"Name\":\"Sql\"," + + "\"SkillSet\":[\"CSharp\",\"Sql\"],\"Gender\":\"Female\",\"AccessLevel\":\"Execute\",\"FavoriteSports\":{\"Sport\":\"Football\"},\"Friends\":[{\"Id\":2,\"Name\":\"Test1\",\"Age\":0}]," + + "\"NewFriends\":[{\"Id\":1,\"Name\":\"NewFriendTest1\",\"Age\":33}],\"UnTypedFriends\":[]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected, json.ToString()); } //Assert @@ -679,14 +733,16 @@ public async Task PatchEmployee_WithODataBind() requestForPost.Content = stringContent; //Act & Assert + var expected = "/convention/$metadata#Employees(Friends(),NewFriends(),UnTypedFriends())/$entity\",\"ID\":1,\"Name\":\"Bind1\"," + + "\"SkillSet\":[\"CSharp\",\"Sql\"],\"Gender\":\"Female\",\"AccessLevel\":\"Execute\",\"FavoriteSports\":{\"Sport\":\"Football\"},\"Friends\":[{\"Id\":3,\"Name\":null,\"Age\":0}]," + + "\"NewFriends\":[{\"Id\":1,\"Name\":\"NewFriendTest1\",\"Age\":33}],\"UnTypedFriends\":[]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected, json.ToString()); } - - - } @@ -827,15 +883,20 @@ public async Task PatchCompanies_WithUpdates_ODataId() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; //Act & Assert + var expected = "/convention/$metadata#Companies/$delta\",\"value\":[{\"Id\":1,\"Name\":\"Company01\",\"OverdueOrders@delta\":" + + "[{\"Id\":1,\"Price\":0,\"Quantity\":9,\"Container\":null}]}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected, json.ToString()); } @@ -857,15 +918,20 @@ public async Task PatchCompanies_WithUpdates_ODataId_WithCast() var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; //Act & Assert + var expected = "$delta\",\"value\":[{\"Id\":1,\"Name\":\"Company02\",\"MyOverdueOrders@delta\":" + + "[{\"Id\":2,\"Price\":0,\"Quantity\":9,\"Container\":null}]}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(expected, json.ToString()); } @@ -886,17 +952,21 @@ public async Task PatchUntypedEmployee_WithOdataId() }"; var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri); + requestForPost.Headers.Add("OData-Version", "4.01"); StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json"); requestForPost.Content = stringContent; Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*"""); //Act & Assert + var expected = "/convention/$metadata#UnTypedEmployees/$delta\",\"value\":[{\"ID\":1,\"Name\":\"Employeeabcd\"," + + "\"UnTypedFriends@delta\":[{\"Id\":1,\"Name\":\"abcd\",\"Age\":0}]}]}"; + using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost)) { var json = response.Content.ReadAsStringAsync().Result; Assert.Equal(HttpStatusCode.OK, response.StatusCode); - json.ToString().Contains("$deletedEntity"); + Assert.Contains(expected, json.ToString()); } }