diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Submit/ChangeSetInitializer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Submit/ChangeSetInitializer.cs
index 3c6d5db1..4918a3ac 100644
--- a/src/Microsoft.Restier.Providers.EntityFramework/Submit/ChangeSetInitializer.cs
+++ b/src/Microsoft.Restier.Providers.EntityFramework/Submit/ChangeSetInitializer.cs
@@ -220,7 +220,7 @@ private void SetValues(DbEntityEntry dbEntry, DataModificationItem item, Type re
propertyPair.Key));
}
- value = Activator.CreateInstance(type);
+ value = propertyEntry.CurrentValue;
SetValues(value, type, dic);
}
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/E2ETestBase.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/E2ETestBase.cs
index 2f212378..e2855c5f 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/E2ETestBase.cs
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/E2ETestBase.cs
@@ -155,6 +155,21 @@ protected void TestPostStatusCodeIs(string uriStringAfterServiceRoot, int status
Assert.Equal(statusCode, response.StatusCode);
}
}
+
+ protected async void TestPatchStatusCodeIs(string uriStringAfterServiceRoot, string patchContent, HttpStatusCode statusCode)
+ {
+ var requestUri = string.Format("{0}/{1}", this.ServiceBaseUri, uriStringAfterServiceRoot);
+ var request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
+
+ request.Content = new StringContent(patchContent);
+ request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
+
+ HttpClient client = new HttpClient();
+ HttpResponseMessage response = await client.SendAsync(request);
+
+ Assert.Equal(statusCode, response.StatusCode);
+ }
+
#endregion
protected void ResetDataSource()
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/Microsoft.OData.Service.Sample.Tests.csproj b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/Microsoft.OData.Service.Sample.Tests.csproj
index 1938e67e..d845fe7a 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/Microsoft.OData.Service.Sample.Tests.csproj
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/Microsoft.OData.Service.Sample.Tests.csproj
@@ -80,6 +80,7 @@
..\..\..\packages\FluentAssertions.4.13.0\lib\net45\FluentAssertions.Core.dll
True
+
..\..\..\packages\Microsoft.OData.Client.6.15.0\lib\net40\Microsoft.OData.Client.dll
True
@@ -96,6 +97,10 @@
..\..\..\packages\Microsoft.Spatial.6.15.0\lib\portable-net45+win+wpa81\Microsoft.Spatial.dll
True
+
+ ..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
+ True
+
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinE2ETestCases.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinE2ETestCases.cs
index 3f96a6cc..2b617389 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinE2ETestCases.cs
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinE2ETestCases.cs
@@ -5,9 +5,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net;
using Microsoft.OData.Client;
using Microsoft.OData.Core;
using Microsoft.OData.Service.Sample.Trippin.Models;
+using Newtonsoft.Json;
using Xunit;
namespace Microsoft.OData.Service.Sample.Tests
@@ -295,11 +297,11 @@ public void CURDComputedImmutableProperty()
// Query the updated entity
this.TestClientContext.Detach(employee);
employee = this.TestClientContext.Orders.Where(e => e.PersonId == personId && e.OrderId == orderId).First();
-
+
// both computed property and immutable property should not have new value
Assert.Equal(400, employee.Price);
Assert.NotEqual("ShouldBeIgnored2", employee.ComputedProperty);
-
+
// Immutable property has value set during insert.
Assert.NotEqual("ShouldBeIgnored2", employee.ImmutableProperty);
Assert.Equal("ShouldNotBeIgnored", employee.ImmutableProperty);
@@ -409,9 +411,9 @@ public void UQProperty()
Assert.Equal("Cooper", lastName);
// Update a property
- Dictionary headers = new Dictionary()
+ Dictionary headers = new Dictionary()
{
- { "Content-Type", "application/json" }
+ { "Content-Type", "application/json" }
};
HttpWebRequestMessage request = new HttpWebRequestMessage(
@@ -566,12 +568,12 @@ public void QueryOptions()
// skip
people2 = this.TestClientContext.People.Skip((int)(personId - 1)).ToList();
Assert.Equal(personId, people2.First().PersonId);
-
+
// count
var countQuery = this.TestClientContext.People.IncludeTotalCount().Skip(1).Take(2) as DataServiceQuery;
var response = countQuery.Execute() as QueryOperationResponse;
Assert.Equal(response.TotalCount, 14);
-
+
// count with expand
countQuery = this.TestClientContext.People.IncludeTotalCount().Expand("Friends").Skip(1).Take(2) as DataServiceQuery;
response = countQuery.Execute() as QueryOperationResponse;
@@ -638,7 +640,7 @@ public void FilterBuiltInDateFunctions()
Assert.True(flight1.All(f => f.StartsAt.Second == startDate.Second));
// Following built-in functions are not supported now.
- // fractionalseconds
+ // fractionalseconds
// date
// time
// totaloffsetminutes
@@ -1119,5 +1121,32 @@ public void ConventionBasedChangeSetAuthorizerTest()
"The current user does not have permission to delete entities from the EntitySet 'Trips'.",
clientException.Message);
}
+
+ [Fact]
+ public void TestPatchSuccessfully()
+ {
+ // Get origin content.
+ var uriStringAfterServiceRoot = "Orders(PersonId=1, OrderId=1)";
+ var originContent = default(string);
+ Action getContent = p => originContent = p;
+ TestGetPayload(uriStringAfterServiceRoot, getContent);
+
+ // Patch it.
+ var changedDescription = "TestDescription";
+ var changedNormalProperty = "TestNormalProperty";
+ string patchContent =
+ string.Format(
+ "{{\n \"Description\": \"{0}\",\n \"NormalOrderDetail\": {{\n \"NormalProperty\": \"{1}\"\n }}\n}}",
+ changedDescription,
+ changedNormalProperty);
+ TestPatchStatusCodeIs(uriStringAfterServiceRoot, patchContent, HttpStatusCode.NoContent);
+
+ // Test patch results.
+ dynamic content = JsonConvert.DeserializeObject(originContent);
+ content.Description = changedDescription;
+ content.NormalOrderDetail.NormalProperty = changedNormalProperty;
+ string changedContent = JsonConvert.SerializeObject(content);
+ TestGetPayloadContains(uriStringAfterServiceRoot, changedContent);
+ }
}
}
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinInMemoryE2ETest.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinInMemoryE2ETest.cs
index bcc6d1e0..ddc1b1f1 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinInMemoryE2ETest.cs
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/TrippinInMemoryE2ETest.cs
@@ -1,6 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
+using System;
+using System.Net;
+using System.Text.RegularExpressions;
+using Newtonsoft.Json;
using Xunit;
namespace Microsoft.OData.Service.Sample.Tests
@@ -44,8 +48,8 @@ public class TrippinInMemoryE2ETest : TrippinInMemoryE2ETestBase
// TODO since webapi doesnot handle query with null, the trips here in the datasource are actually not null.
[InlineData("/People('clydeguess')/Trips", 200)]
// collection of navigation property's property and navigation property has null value
- // TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. 500 is returned actually.
- [InlineData("/People('willieashmore')/Friends/MiddleName", 500)]
+ // TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. (404 is returned when key-as-segment, otherwise, 500 will be returned.)
+ [InlineData("/People('willieashmore')/Friends/MiddleName", 404)]
public void QueryPropertyWithNullValueStatusCode(string url, int expectedCode)
{
TestGetStatusCodeIs(url, expectedCode);
@@ -77,8 +81,8 @@ public void QueryPropertyWithNullValueStatusCode(string url, int expectedCode)
// collection of navigation property
[InlineData("/People('NoneExist')/Friends", 404)]
// collection of navigation property's property
- // TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. 500 is returned actually.
- [InlineData("/People('NoneExist')/Friends/MiddleName", 500)]
+ // TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. (404 is returned when key-as-segment, otherwise, 500 will be returned.)
+ [InlineData("/People('NoneExist')/Friends/MiddleName", 404)]
public void QueryPropertyWithNonExistEntity(string url, int expectedCode)
{
TestGetStatusCodeIs(url, expectedCode);
@@ -158,5 +162,46 @@ public void TestRawValuedEnumPropertyAccess()
{
TestGetPayloadIs("People('russellwhyte')/FavoriteFeature/$value", "Feature1");
}
+
+ [Fact]
+ public void TestPatchSuccessfully()
+ {
+ // Get origin content and sessionId.
+ var uriStringAfterServiceRoot = "Airports('KLAX')";
+ var originContent = default(string);
+ Action getContent = p => originContent = p;
+ TestGetPayload(uriStringAfterServiceRoot, getContent);
+ var sessionId = GetSessionIdFromResponse(originContent);
+ Assert.NotNull(sessionId);
+
+ // Patch it.
+ uriStringAfterServiceRoot = string.Format(@"(S({0}))/{1}", sessionId, uriStringAfterServiceRoot);
+ var changedRegion = "TestRegion";
+ var changedAddress = "1 World Way, Los Angeles, CA, 90045";
+ string patchContent =
+ string.Format(
+ "{{\r\n \"Location\":{{\r\n \"Address\":\"{0}\",\r\n \"City\":{{\r\n \"Region\":\"{1}\"\r\n }}\r\n }}\r\n}}",
+ changedAddress,
+ changedRegion);
+ TestPatchStatusCodeIs(uriStringAfterServiceRoot, patchContent, HttpStatusCode.NoContent);
+
+ // Test patch results.
+ dynamic content = JsonConvert.DeserializeObject(originContent);
+ content.Location.Address = changedAddress;
+ content.Location.City.Region = changedRegion;
+ string changedContent = JsonConvert.SerializeObject(content);
+ TestGetPayloadContains(uriStringAfterServiceRoot, changedContent);
+ }
+
+ private static string GetSessionIdFromResponse(string response)
+ {
+ var match = Regex.Match(response, @"/\(S\((\w+)\)\)");
+ if (match.Success)
+ {
+ return match.Groups[1].Value;
+ }
+
+ return default(string);
+ }
}
}
\ No newline at end of file
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/packages.config b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/packages.config
index 740423ac..ad199180 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/packages.config
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Tests/packages.config
@@ -5,6 +5,7 @@
+
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/OrderDetail.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/OrderDetail.cs
index cbd6b680..507d40cd 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/OrderDetail.cs
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/OrderDetail.cs
@@ -16,6 +16,8 @@ public OrderDetail()
public string NormalProperty { get; set; }
+ public string AnotherNormalProperty { get; set; }
+
public string ComputedProperty { get; set; }
public string ImmutableProperty { get; set; }
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/TrippinModel.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/TrippinModel.cs
index 3128f7c7..0b563aaf 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/TrippinModel.cs
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Models/TrippinModel.cs
@@ -563,7 +563,11 @@ public static void ResetDataSource()
OrderId = 1,
Description = "Person 1 Order 1",
Price = 200,
- NormalOrderDetail = new OrderDetail(),
+ NormalOrderDetail = new OrderDetail()
+ {
+ NormalProperty = "NormalProperty",
+ AnotherNormalProperty = "AnotherNormalProperty"
+ },
ComputedOrderDetail = new OrderDetail(),
ImmutableOrderDetail = new OrderDetail()
},
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/App_Start/WebApiConfig.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/App_Start/WebApiConfig.cs
index ace3158f..d5908dfc 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/App_Start/WebApiConfig.cs
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/App_Start/WebApiConfig.cs
@@ -3,6 +3,7 @@
using System.Web.Http;
using System.Web.OData;
+using System.Web.OData.Extensions;
using Microsoft.OData.Service.Sample.TrippinInMemory.Api;
using Microsoft.Restier.Publishers.OData;
using Microsoft.Restier.Publishers.OData.Batch;
@@ -13,9 +14,10 @@ public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
- RegisterTrippin(config, GlobalConfiguration.DefaultServer);
config.SetUseVerboseErrors(true);
config.MessageHandlers.Add(new ETagMessageHandler());
+ config.SetUrlKeyDelimiter(ODataUrlKeyDelimiter.Slash);
+ RegisterTrippin(config, GlobalConfiguration.DefaultServer);
}
public static async void RegisterTrippin(
diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/Web.config b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/Web.config
index d008b610..b337d7ed 100644
--- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/Web.config
+++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.TrippinInMemory/Web.config
@@ -28,6 +28,12 @@
+
+
+
+
+
+
diff --git a/test/ODataEndToEnd/Microsoft.Restier.Providers.InMemory/Submit/ChangeSetInitializer.cs b/test/ODataEndToEnd/Microsoft.Restier.Providers.InMemory/Submit/ChangeSetInitializer.cs
index de00f286..ec7bc2a9 100644
--- a/test/ODataEndToEnd/Microsoft.Restier.Providers.InMemory/Submit/ChangeSetInitializer.cs
+++ b/test/ODataEndToEnd/Microsoft.Restier.Providers.InMemory/Submit/ChangeSetInitializer.cs
@@ -202,7 +202,7 @@ private static void SetValues(object instance, Type type, IReadOnlyDictionary