diff --git a/src/Microsoft.AspNet.OData.Shared/Common/ODataPathHelper.cs b/src/Microsoft.AspNet.OData.Shared/Common/ODataPathHelper.cs
new file mode 100644
index 0000000000..1e4c27b23a
--- /dev/null
+++ b/src/Microsoft.AspNet.OData.Shared/Common/ODataPathHelper.cs
@@ -0,0 +1,70 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (c) .NET Foundation and Contributors. All rights reserved.
+// See License.txt in the project root for license information.
+//
+//------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.OData.UriParser;
+
+namespace Microsoft.AspNet.OData.Common
+{
+ ///
+ /// Helper methods for .
+ ///
+ internal static class ODataPathHelper
+ {
+ ///
+ /// Get the keys from a .
+ ///
+ /// The to extract the keys.
+ /// Dictionary of keys.
+ public static Dictionary KeySegmentAsDictionary(KeySegment keySegment)
+ {
+ if (keySegment == null)
+ {
+ throw Error.ArgumentNull(nameof(keySegment));
+ }
+
+ return keySegment.Keys.ToDictionary(d => d.Key, d => d.Value);
+ }
+
+ ///
+ /// Get the position of the next in a list of .
+ ///
+ /// List of .
+ /// Current position in the list of .
+ /// Position of the next if it exists, or -1 otherwise.
+ public static int GetNextKeySegmentPosition(IReadOnlyList pathSegments, int currentPosition)
+ {
+ if (pathSegments == null)
+ {
+ throw Error.ArgumentNull(nameof(pathSegments));
+ }
+
+ if (currentPosition < 0 || currentPosition >= pathSegments.Count)
+ {
+ return -1;
+ }
+
+ if (pathSegments[currentPosition] is KeySegment)
+ {
+ currentPosition++;
+ }
+
+ for (int i = currentPosition; i < pathSegments.Count; i++)
+ {
+ ODataPathSegment currentSegment = pathSegments[i];
+
+ if (currentSegment is KeySegment)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.OData.Shared/Extensions/ODataPathExtensions.cs b/src/Microsoft.AspNet.OData.Shared/Extensions/ODataPathExtensions.cs
index 54808127e9..70467cf20d 100644
--- a/src/Microsoft.AspNet.OData.Shared/Extensions/ODataPathExtensions.cs
+++ b/src/Microsoft.AspNet.OData.Shared/Extensions/ODataPathExtensions.cs
@@ -6,51 +6,84 @@
//------------------------------------------------------------------------------
using System.Collections.Generic;
+using System.Linq;
using Microsoft.AspNet.OData.Common;
using Microsoft.OData.UriParser;
namespace Microsoft.AspNet.OData.Extensions
{
+ ///
+ /// Extensions method for .
+ ///
internal static class ODataPathExtensions
{
- public static Dictionary GetKeys(this ODataPath path)
+ ///
+ /// Get keys from the last .
+ ///
+ /// .
+ /// Dictionary of keys.
+ internal static Dictionary GetKeys(this ODataPath path)
{
+ Dictionary keys = new Dictionary();
+
if (path == null)
{
throw Error.ArgumentNull(nameof(path));
}
- Dictionary keys = new Dictionary();
-
- // Books(1)/Authors(1000)/Namespace.SpecialAuthor
- if (path.LastSegment is TypeSegment)
+ if (path.Count == 0)
{
- ODataPath pathWithoutLastSegmentCastType = path.TrimEndingTypeSegment();
-
- if (pathWithoutLastSegmentCastType.LastSegment is KeySegment)
- {
- keys = GetKeysFromKeySegment(pathWithoutLastSegmentCastType.LastSegment as KeySegment);
- }
+ return keys;
}
- // Books(1)/Authors/Namespace.SpecialAuthor/(1000)
- else if (path.LastSegment is KeySegment)
+
+ List pathSegments = path.AsList();
+
+ KeySegment keySegment = pathSegments.OfType().LastOrDefault();
+
+ if (keySegment == null)
{
- keys = GetKeysFromKeySegment(path.LastSegment as KeySegment);
+ return keys;
}
+ keys = ODataPathHelper.KeySegmentAsDictionary(keySegment);
+
return keys;
}
- private static Dictionary GetKeysFromKeySegment(KeySegment keySegment)
+ ///
+ /// Return the last segment in the path, which is not a or .
+ ///
+ /// The .
+ /// An .
+ public static ODataPathSegment GetLastNonTypeNonKeySegment(this ODataPath path)
{
- Dictionary keys = new Dictionary();
+ if (path == null)
+ {
+ throw Error.ArgumentNull(nameof(path));
+ }
+
+ // If the path is Employees(2)/NewFriends(2)/Namespace.MyNewFriend where Namespace.MyNewFriend is a type segment,
+ // This method will return NewFriends NavigationPropertySegment.
- foreach (KeyValuePair kvp in keySegment.Keys)
+ List pathSegments = path.AsList();
+ int position = path.Count - 1;
+
+ while (position >= 0 && (pathSegments[position] is TypeSegment || pathSegments[position] is KeySegment))
{
- keys.Add(kvp.Key, kvp.Value);
+ --position;
}
- return keys;
+ return position < 0 ? null : pathSegments[position];
+ }
+
+ ///
+ /// Returns a list of in an .
+ ///
+ /// The .
+ /// List of .
+ public static List GetSegments(this ODataPath path)
+ {
+ return path.AsList();
}
}
}
diff --git a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems
index 5848fb87c9..4b881021d9 100644
--- a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems
+++ b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems
@@ -25,6 +25,7 @@
+
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore/Microsoft.Test.E2E.AspNetCore.OData.csproj b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore/Microsoft.Test.E2E.AspNetCore.OData.csproj
index 4603d42eeb..53b72ef509 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore/Microsoft.Test.E2E.AspNetCore.OData.csproj
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore/Microsoft.Test.E2E.AspNetCore.OData.csproj
@@ -1402,30 +1402,21 @@
OpenType\TypedTest.cs
-
- BulkOperation\BulkInsertDataModel.cs
+
+ BulkOperation\BulkOperationTest.cs
-
- BulkOperation\BulkInsertEdmModel.cs
+
+ BulkOperation\BulkOperationDataModel.cs
-
- BulkOperation\BulkInsertController.cs
+
+ BulkOperation\BulkOperationEdmModel.cs
+
+
+ BulkOperation\BulkOperationController.cs
BulkOperation\BulkOperationPatchHandlers.cs
-
- BulkOperation\BulkInsertDataModel.cs
-
-
- BulkOperation\BulkInsertEdmModel.cs
-
-
- BulkOperation\BulkInsertController.cs
-
-
- BulkOperation\BulkOperationPatchHandlers.cs
-
ParameterAlias\ParameterAliasDataSource.cs
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationControllerEF.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationControllerEF.cs
index c18760bd20..aa87466576 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationControllerEF.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationControllerEF.cs
@@ -13,25 +13,24 @@
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Routing;
using Microsoft.EntityFrameworkCore;
-using Microsoft.Test.E2E.AspNet.OData.BulkOperation;
using Microsoft.Test.E2E.AspNet.OData.Common.Controllers;
using Xunit;
using static Microsoft.Test.E2E.AspNet.OData.BulkOperation.APIHandlerFactoryEF;
-namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
+namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
public class EmployeesControllerEF : TestODataController
{
public EmployeesControllerEF()
{
-
+
}
-
+
public static List employees;
public static List friends;
internal DbSet GenerateData(EmployeeDBContext context)
- {
+ {
if (context.Employees.Any())
{
return context.Employees;
@@ -59,7 +58,7 @@ internal DbSet GenerateDataOrders(EmployeeDBContext context)
}
friends = new List();
- friends.Add(new Friend { Id = 1, Age = 10 , Orders = new List() { new Order { Id = 1, Price = 5 }, new Order { Id = 2, Price = 5 } } });
+ friends.Add(new Friend { Id = 1, Age = 10, Orders = new List() { new Order { Id = 1, Price = 5 }, new Order { Id = 2, Price = 5 } } });
friends.Add(new Friend { Id = 2, Age = 20, Orders = new List() { new Order { Id = 10, Price = 5 }, new Order { Id = 20, Price = 5 } } });
friends.Add(new Friend { Id = 3, Age = 30, Orders = new List() { new Order { Id = 3, Price = 5 }, new Order { Id = 4, Price = 5 } } });
friends.Add(new Friend { Id = 4, Age = 40, Orders = new List() { new Order { Id = 30, Price = 5 }, new Order { Id = 40, Price = 5 } } });
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationPatchHandlersEF.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationPatchHandlersEF.cs
index 7f95a5961c..a09f22adeb 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationPatchHandlersEF.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationPatchHandlersEF.cs
@@ -11,7 +11,7 @@
using Microsoft.AspNet.OData;
using Microsoft.EntityFrameworkCore;
using Microsoft.OData.Edm;
-using Microsoft.Test.E2E.AspNet.OData.BulkInsert;
+using Microsoft.OData.UriParser;
namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
@@ -47,11 +47,9 @@ protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuild
modelBuilder.Entity().Ignore(c => c.Container);
modelBuilder.Entity().Ignore(c => c.Container);
}
-
-
}
- internal class APIHandlerFactoryEF: ODataAPIHandlerFactory
+ internal class APIHandlerFactoryEF : ODataAPIHandlerFactory
{
EmployeeDBContext dbContext;
@@ -60,285 +58,275 @@ public APIHandlerFactoryEF(IEdmModel model, EmployeeDBContext dbContext) : base(
this.dbContext = dbContext;
}
- public override IODataAPIHandler GetHandler(NavigationPath navigationPath)
+ public override IODataAPIHandler GetHandler(ODataPath odataPath)
{
- if (navigationPath != null)
+ if (odataPath != null)
{
- var pathItems = navigationPath;
+ var pathItems = odataPath;
}
return null;
- }
-
- internal class EmployeeEFPatchHandler : ODataAPIHandler
- {
- EmployeeDBContext dbContext = null;
-
- public EmployeeEFPatchHandler(EmployeeDBContext dbContext)
- {
- this.dbContext = dbContext;
}
- public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out Employee createdObject, out string errorMessage)
+ internal class EmployeeEFPatchHandler : ODataAPIHandler
{
- createdObject = null;
- errorMessage = string.Empty;
+ EmployeeDBContext dbContext = null;
- try
+ public EmployeeEFPatchHandler(EmployeeDBContext dbContext)
{
- createdObject = new Employee();
- dbContext.Employees.Add(createdObject);
-
- return ODataAPIResponseStatus.Success;
+ this.dbContext = dbContext;
}
- catch (Exception ex)
+
+ public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out Employee createdObject, out string errorMessage)
{
- errorMessage = ex.Message;
+ createdObject = null;
+ errorMessage = string.Empty;
- return ODataAPIResponseStatus.Failure;
- }
- }
+ try
+ {
+ createdObject = new Employee();
+ dbContext.Employees.Add(createdObject);
- public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
- {
- errorMessage = string.Empty;
+ return ODataAPIResponseStatus.Success;
+ }
+ catch (Exception ex)
+ {
+ errorMessage = ex.Message;
+
+ return ODataAPIResponseStatus.Failure;
+ }
+ }
- try
+ public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
{
- var id = keyValues.First().Value.ToString();
- var customer = dbContext.Employees.First(x => x.ID == Int32.Parse(id));
+ errorMessage = string.Empty;
- dbContext.Employees.Remove(customer);
+ try
+ {
+ var id = keyValues.First().Value.ToString();
+ var customer = dbContext.Employees.First(x => x.ID == Int32.Parse(id));
- return ODataAPIResponseStatus.Success;
- }
- catch (Exception ex)
- {
- errorMessage = ex.Message;
+ dbContext.Employees.Remove(customer);
- return ODataAPIResponseStatus.Failure;
- }
- }
+ return ODataAPIResponseStatus.Success;
+ }
+ catch (Exception ex)
+ {
+ errorMessage = ex.Message;
- public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out Employee originalObject, out string errorMessage)
- {
- ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
- errorMessage = string.Empty;
- originalObject = null;
+ return ODataAPIResponseStatus.Failure;
+ }
+ }
- try
+ public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out Employee originalObject, out string errorMessage)
{
- var id = keyValues["ID"].ToString();
- originalObject = dbContext.Employees.First(x => x.ID == Int32.Parse(id));
+ ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
+ errorMessage = string.Empty;
+ originalObject = null;
+ try
+ {
+ var id = keyValues["ID"].ToString();
+ originalObject = dbContext.Employees.First(x => x.ID == Int32.Parse(id));
+
+ if (originalObject == null)
+ {
+ status = ODataAPIResponseStatus.NotFound;
+ }
- if (originalObject == null)
+ }
+ catch (Exception ex)
{
- status = ODataAPIResponseStatus.NotFound;
+ status = ODataAPIResponseStatus.Failure;
+ errorMessage = ex.Message;
}
+ return status;
}
- catch (Exception ex)
+
+ public override IODataAPIHandler GetNestedHandler(Employee parent, string navigationPropertyName)
{
- status = ODataAPIResponseStatus.Failure;
- errorMessage = ex.Message;
+ switch (navigationPropertyName)
+ {
+ case "Friends":
+ return new FriendEFPatchHandler(parent);
+ case "NewFriends":
+ return new NewFriendEFPatchHandler(parent);
+ default:
+ return null;
+ }
}
-
- return status;
}
- public override IODataAPIHandler GetNestedHandler(Employee parent, string navigationPropertyName)
+ internal class FriendEFPatchHandler : ODataAPIHandler
{
- switch (navigationPropertyName)
+ Employee employee;
+ public FriendEFPatchHandler(Employee employee)
{
- case "Friends":
- return new FriendEFPatchHandler(parent);
- case "NewFriends":
- return new NewFriendEFPatchHandler(parent);
- default:
- return null;
+ this.employee = employee;
}
-
- }
-
- }
- internal class FriendEFPatchHandler : ODataAPIHandler
- {
- Employee employee;
- public FriendEFPatchHandler(Employee employee)
- {
- this.employee = employee;
- }
-
- public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out Friend createdObject, out string errorMessage)
- {
- createdObject = null;
- errorMessage = string.Empty;
-
- try
+ public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out Friend createdObject, out string errorMessage)
{
- createdObject = new Friend();
- employee.Friends.Add(createdObject);
+ createdObject = null;
+ errorMessage = string.Empty;
- return ODataAPIResponseStatus.Success;
- }
- catch (Exception ex)
- {
- errorMessage = ex.Message;
+ try
+ {
+ createdObject = new Friend();
+ employee.Friends.Add(createdObject);
- return ODataAPIResponseStatus.Failure;
- }
- }
+ return ODataAPIResponseStatus.Success;
+ }
+ catch (Exception ex)
+ {
+ errorMessage = ex.Message;
- public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
- {
- errorMessage = string.Empty;
+ return ODataAPIResponseStatus.Failure;
+ }
+ }
- try
+ public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
{
- var id = keyValues.First().Value.ToString();
- var friend = employee.Friends.First(x => x.Id == Int32.Parse(id));
+ errorMessage = string.Empty;
- employee.Friends.Remove(friend);
+ try
+ {
+ var id = keyValues.First().Value.ToString();
+ var friend = employee.Friends.First(x => x.Id == Int32.Parse(id));
- return ODataAPIResponseStatus.Success;
- }
- catch (Exception ex)
- {
- errorMessage = ex.Message;
+ employee.Friends.Remove(friend);
- return ODataAPIResponseStatus.Failure;
- }
- }
+ return ODataAPIResponseStatus.Success;
+ }
+ catch (Exception ex)
+ {
+ errorMessage = ex.Message;
- public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out Friend originalObject, out string errorMessage)
- {
- ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
- errorMessage = string.Empty;
- originalObject = null;
+ return ODataAPIResponseStatus.Failure;
+ }
+ }
- try
+ public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out Friend originalObject, out string errorMessage)
{
- var id = keyValues["Id"].ToString();
- if (employee.Friends == null)
- {
- status = ODataAPIResponseStatus.NotFound;
- }
- else
+ ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
+ errorMessage = string.Empty;
+ originalObject = null;
+
+ try
{
- originalObject = employee.Friends.FirstOrDefault(x => x.Id == Int32.Parse(id));
- }
+ var id = keyValues["Id"].ToString();
+ if (employee.Friends == null)
+ {
+ status = ODataAPIResponseStatus.NotFound;
+ }
+ else
+ {
+ originalObject = employee.Friends.FirstOrDefault(x => x.Id == Int32.Parse(id));
+ }
+ if (originalObject == null)
+ {
+ status = ODataAPIResponseStatus.NotFound;
+ }
- if (originalObject == null)
+ }
+ catch (Exception ex)
{
- status = ODataAPIResponseStatus.NotFound;
+ status = ODataAPIResponseStatus.Failure;
+ errorMessage = ex.Message;
}
+ return status;
}
- catch (Exception ex)
+
+ public override IODataAPIHandler GetNestedHandler(Friend parent, string navigationPropertyName)
{
- status = ODataAPIResponseStatus.Failure;
- errorMessage = ex.Message;
+ return new OrderEFPatchHandler(parent);
}
-
- return status;
}
- public override IODataAPIHandler GetNestedHandler(Friend parent, string navigationPropertyName)
+ internal class NewFriendEFPatchHandler : ODataAPIHandler
{
- return new OrderEFPatchHandler(parent);
- }
-
- }
-
-
- internal class NewFriendEFPatchHandler : ODataAPIHandler
- {
- Employee employee;
- public NewFriendEFPatchHandler(Employee employee)
- {
- this.employee = employee;
- }
-
- public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out NewFriend createdObject, out string errorMessage)
- {
- createdObject = null;
- errorMessage = string.Empty;
-
- try
+ Employee employee;
+ public NewFriendEFPatchHandler(Employee employee)
{
- createdObject = new NewFriend();
- employee.NewFriends.Add(createdObject);
-
- return ODataAPIResponseStatus.Success;
+ this.employee = employee;
}
- catch (Exception ex)
+
+ public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out NewFriend createdObject, out string errorMessage)
{
- errorMessage = ex.Message;
+ createdObject = null;
+ errorMessage = string.Empty;
- return ODataAPIResponseStatus.Failure;
- }
- }
+ try
+ {
+ createdObject = new NewFriend();
+ employee.NewFriends.Add(createdObject);
- public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
- {
- errorMessage = string.Empty;
+ return ODataAPIResponseStatus.Success;
+ }
+ catch (Exception ex)
+ {
+ errorMessage = ex.Message;
- try
+ return ODataAPIResponseStatus.Failure;
+ }
+ }
+
+ public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
{
- var id = keyValues.First().Value.ToString();
- var friend = employee.NewFriends.First(x => x.Id == Int32.Parse(id));
+ errorMessage = string.Empty;
- employee.NewFriends.Remove(friend);
+ try
+ {
+ var id = keyValues.First().Value.ToString();
+ var friend = employee.NewFriends.First(x => x.Id == Int32.Parse(id));
- return ODataAPIResponseStatus.Success;
- }
- catch (Exception ex)
- {
- errorMessage = ex.Message;
+ employee.NewFriends.Remove(friend);
- return ODataAPIResponseStatus.Failure;
- }
- }
+ return ODataAPIResponseStatus.Success;
+ }
+ catch (Exception ex)
+ {
+ errorMessage = ex.Message;
- public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out NewFriend originalObject, out string errorMessage)
- {
- ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
- errorMessage = string.Empty;
- originalObject = null;
+ return ODataAPIResponseStatus.Failure;
+ }
+ }
- try
+ public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out NewFriend originalObject, out string errorMessage)
{
- var id = keyValues["Id"].ToString();
- originalObject = employee.NewFriends.First(x => x.Id == Int32.Parse(id));
+ ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
+ errorMessage = string.Empty;
+ originalObject = null;
+ try
+ {
+ var id = keyValues["Id"].ToString();
+ originalObject = employee.NewFriends.First(x => x.Id == Int32.Parse(id));
+
+ if (originalObject == null)
+ {
+ status = ODataAPIResponseStatus.NotFound;
+ }
- if (originalObject == null)
+ }
+ catch (Exception ex)
{
- status = ODataAPIResponseStatus.NotFound;
+ status = ODataAPIResponseStatus.Failure;
+ errorMessage = ex.Message;
}
+ return status;
}
- catch (Exception ex)
+
+ public override IODataAPIHandler GetNestedHandler(NewFriend parent, string navigationPropertyName)
{
- status = ODataAPIResponseStatus.Failure;
- errorMessage = ex.Message;
+ return null;
}
-
- return status;
- }
-
- public override IODataAPIHandler GetNestedHandler(NewFriend parent, string navigationPropertyName)
- {
- return null;
}
- }
-
-
-
internal class OrderEFPatchHandler : ODataAPIHandler
{
Friend friend;
@@ -399,12 +387,10 @@ public override ODataAPIResponseStatus TryGet(IDictionary keyVal
var id = keyValues["Id"].ToString();
originalObject = friend.Orders.First(x => x.Id == Int32.Parse(id));
-
if (originalObject == null)
{
status = ODataAPIResponseStatus.NotFound;
}
-
}
catch (Exception ex)
{
@@ -420,6 +406,5 @@ public override IODataAPIHandler GetNestedHandler(Order parent, string navigatio
throw new NotImplementedException();
}
}
-
}
}
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkInsertTestEF.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationTestEF.cs
similarity index 91%
rename from test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkInsertTestEF.cs
rename to test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationTestEF.cs
index dbeed96620..eed66da892 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkInsertTestEF.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/BulkOperation/EFTests/BulkOperationTestEF.cs
@@ -1,49 +1,44 @@
//-----------------------------------------------------------------------------
-//
+//
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
//
//------------------------------------------------------------------------------
-using System;
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNet.OData.Routing.Conventions;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Test.E2E.AspNet.OData.BulkOperation;
using Microsoft.Test.E2E.AspNet.OData.Common.Execution;
using Microsoft.Test.E2E.AspNet.OData.Common.Extensions;
using Newtonsoft.Json.Linq;
using Xunit;
-namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
+namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
- public class BulkInsertTestEF : WebHostTestBase
+ public class BulkOperationTestEF : WebHostTestBase
{
- public BulkInsertTestEF(WebHostTestFixture fixture)
- :base(fixture)
- {
+ public BulkOperationTestEF(WebHostTestFixture fixture)
+ : base(fixture)
+ {
}
protected override void UpdateConfiguration(WebRouteConfiguration configuration)
{
var controllers = new[] { typeof(EmployeesControllerEF), typeof(MetadataController) };
configuration.AddControllers(controllers);
-
+
configuration.Routes.Clear();
configuration.Count().Filter().OrderBy().Expand().MaxTop(null).Select();
- configuration.MapODataServiceRoute("convention", "convention", BulkInsertEdmModel.GetConventionModel(configuration));
- configuration.MapODataServiceRoute("explicit", "explicit", BulkInsertEdmModel.GetExplicitModel(configuration), new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault());
+ configuration.MapODataServiceRoute("convention", "convention", BulkOperationEdmModel.GetConventionModel(configuration));
+ configuration.MapODataServiceRoute("explicit", "explicit", BulkOperationEdmModel.GetExplicitModel(configuration), new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault());
configuration.EnsureInitialized();
-
+
}
@@ -68,11 +63,10 @@ public async Task PatchEmployee_WithUpdates()
//Act & Assert
using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost))
{
- var json = await response.Content.ReadAsObject();
+ var json = await response.Content.ReadAsObject();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("Sql", json.ToString());
}
-
}
[Fact]
@@ -99,22 +93,21 @@ public async Task PatchEmployee_WithUpdates_WithEmployees()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains("SqlFU", json.ToString());
}
-
}
[Fact]
public async Task PatchEmployee_WithUpdates_Employees()
{
//Arrange
-
+
string requestUri = this.BaseAddress + "/convention/Employees";
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees/$delta',
- 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Employee', 'ID':1,'Name':'Employee1',
+ 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Employee', 'ID':1,'Name':'Employee1',
'Friends@odata.delta':[{'Id':1,'Name':'Friend1',
'Orders@odata.delta' :[{'Id':1,'Price': 10}, {'Id':2,'Price': 20} ] },{'Id':2,'Name':'Friend2'}]
},
- { '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Employee', 'ID':2,'Name':'Employee2',
+ { '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Employee', 'ID':2,'Name':'Employee2',
'Friends@odata.delta':[{'Id':3,'Name':'Friend3',
'Orders@odata.delta' :[{'Id':3,'Price': 30}, {'Id':4,'Price': 40} ]},{'Id':4,'Name':'Friend4'}]
}]
@@ -123,7 +116,7 @@ public async Task PatchEmployee_WithUpdates_Employees()
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
requestForPost.Headers.Add("OData-MaxVersion", "4.01");
-
+
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
requestForPost.Content = stringContent;
@@ -141,7 +134,6 @@ public async Task PatchEmployee_WithUpdates_Employees()
Assert.Contains("Employee1", json);
Assert.Contains("Employee2", json);
}
-
}
[Fact]
@@ -151,11 +143,11 @@ public async Task PatchEmployee_WithUpdates_Employees_InV4()
string requestUri = this.BaseAddress + "/convention/Employees";
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees/$delta',
- 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Employee', 'ID':1,'Name':'Employee1',
+ 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Employee', 'ID':1,'Name':'Employee1',
'Friends@odata.delta':[{'Id':1,'Name':'Friend1',
'Orders@odata.delta' :[{'Id':1,'Price': 10}, {'Id':2,'Price': 20} ] },{'Id':2,'Name':'Friend2'}]
},
- { '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Employee', 'ID':2,'Name':'Employee2',
+ { '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Employee', 'ID':2,'Name':'Employee2',
'Friends@odata.delta':[{'Id':3,'Name':'Friend3',
'Orders@odata.delta' :[{'Id':3,'Price': 30}, {'Id':4,'Price': 40} ]},{'Id':4,'Name':'Friend4'}]
}]
@@ -182,15 +174,13 @@ public async Task PatchEmployee_WithUpdates_Employees_InV4()
Assert.Contains("Employee1", json);
Assert.Contains("Employee2", json);
}
-
}
-
[Fact]
public async Task PatchEmployee_WithDelete()
{
//Arrange
-
+
string requestUri = this.BaseAddress + "/convention/Employees(1)";
var content = @"{
@@ -210,11 +200,8 @@ public async Task PatchEmployee_WithDelete()
var json = response.Content.ReadAsStringAsync().Result;
Assert.Contains("Sql", json);
}
-
-
}
-
[Fact]
public async Task PatchEmployee_WithAddUpdateAndDelete()
{
@@ -239,15 +226,13 @@ public async Task PatchEmployee_WithAddUpdateAndDelete()
var json = response.Content.ReadAsStringAsync().Result;
Assert.Contains("SqlUD", json);
}
-
}
-
[Fact]
public async Task PatchEmployee_WithMultipleUpdatesinOrder1()
{
//Arrange
-
+
string requestUri = this.BaseAddress + "/convention/Employees(1)";
var content = @"{
@@ -264,17 +249,16 @@ public async Task PatchEmployee_WithMultipleUpdatesinOrder1()
using (HttpResponseMessage response = await this.Client.SendAsync(requestForPost))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var json = response.Content.ReadAsStringAsync().Result;
+ var json = response.Content.ReadAsStringAsync().Result;
Assert.Contains("SqlMU", json);
}
-
}
[Fact]
public async Task PatchEmployee_WithMultipleUpdatesinOrder2()
{
//Arrange
-
+
string requestUri = this.BaseAddress + "/convention/Employees(1)";
var content = @"{
@@ -294,11 +278,8 @@ public async Task PatchEmployee_WithMultipleUpdatesinOrder2()
var json = response.Content.ReadAsStringAsync().Result;
Assert.Contains("SqlMU1", json);
}
-
}
-
#endregion
-
}
}
\ No newline at end of file
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/Microsoft.Test.E2E.AspNetCore3x.OData.csproj b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/Microsoft.Test.E2E.AspNetCore3x.OData.csproj
index 227ca4e4ad..5ee1f5d514 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/Microsoft.Test.E2E.AspNetCore3x.OData.csproj
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/Build.AspNetCore3x/Microsoft.Test.E2E.AspNetCore3x.OData.csproj
@@ -1665,18 +1665,21 @@
Validation\DeltaOfTValidationTests.cs
-
- BulkOperation\BulkInsertDataModel.cs
-
-
- BulkOperation\BulkInsertEdmModel.cs
-
-
- BulkOperation\BulkInsertController.cs
-
-
- BulkOperation\BulkOperationPatchHandlers.cs
-
+
+ BulkOperation\BulkOperationTest.cs
+
+
+ BulkOperation\BulkOperationDataModel.cs
+
+
+ BulkOperation\BulkOperationEdmModel.cs
+
+
+ BulkOperation\BulkOperationController.cs
+
+
+ BulkOperation\BulkOperationPatchHandlers.cs
+
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationController.cs
similarity index 69%
rename from test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs
rename to test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationController.cs
index b0697cd2e7..bcc043edd6 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertController.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationController.cs
@@ -1,25 +1,22 @@
//-----------------------------------------------------------------------------
-//
+//
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
//
//------------------------------------------------------------------------------
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Routing;
using Microsoft.OData.Edm;
-using Microsoft.Test.E2E.AspNet.OData.BulkOperation;
using Microsoft.Test.E2E.AspNet.OData.Common.Controllers;
using Xunit;
-namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
-{
+namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
+{
public class EmployeesController : TestODataController
{
public EmployeesController()
@@ -39,10 +36,9 @@ public EmployeesController()
private List Friends = null;
-
private void InitEmployees()
{
- Friends = new List { new Friend { Id = 1, Name = "Test0", Age =33 }, new Friend { Id = 2, Name = "Test1", Orders = new List() { new Order { Id = 1, Price = 2 } } }, new Friend { Id = 3, Name = "Test3" }, new Friend { Id = 4, Name = "Test4" } };
+ Friends = new List { new Friend { Id = 1, Name = "Test0", Age = 33 }, new Friend { Id = 2, Name = "Test1", Orders = new List() { new Order { Id = 1, Price = 2 } } }, new Friend { Id = 3, Name = "Test3" }, new Friend { Id = 4, Name = "Test4" } };
Employees = new List
{
@@ -144,20 +140,19 @@ public DeltaSet PatchWithUsersMethod(DeltaSet friendColl,
}
public EdmChangedObjectCollection PatchWithUsersMethodTypeLess(int key, EdmChangedObjectCollection friendColl)
{
-
- var entity = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkInsert.UnTypedEmployee") as IEdmEntityType;
+ var entity = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkOperation.UnTypedEmployee") as IEdmEntityType;
InitTypeLessEmployees(entity);
- var entity1 = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkInsert.UnTypedFriend") as IEdmEntityType;
+ var entity1 = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkOperation.UnTypedFriend") as IEdmEntityType;
- var changedObjColl = friendColl.Patch(new FriendTypelessAPIHandler(EmployeesTypeless[key - 1], entity) ,new TypelessAPIHandlerFactory(Request.GetModel(), entity));
+ var changedObjColl = friendColl.Patch(new FriendTypelessAPIHandler(EmployeesTypeless[key - 1], entity), new TypelessAPIHandlerFactory(Request.GetModel(), entity));
return changedObjColl;
}
public EdmChangedObjectCollection EmployeePatchMethodTypeLess(EdmChangedObjectCollection empColl)
{
- var entity = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkInsert.UnTypedEmployee") as IEdmEntityType;
+ var entity = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkOperation.UnTypedEmployee") as IEdmEntityType;
InitTypeLessEmployees(entity);
var changedObjColl = empColl.Patch(new EmployeeEdmAPIHandler(entity), new TypelessAPIHandlerFactory(Request.GetModel(), entity));
@@ -179,10 +174,11 @@ private void ValidateSuccessfulTypeless()
friends.First().TryGetPropertyValue("Name", out obj1);
object name;
+
if (EmployeesTypeless.First().TryGetPropertyValue("Name", out name) && name.ToString() == "Employeeabcd")
{
Assert.Equal("abcd", obj1.ToString());
-
+
object age;
friends.First().TryGetPropertyValue("Age", out age);
@@ -218,7 +214,7 @@ public ITestActionResult GetFriends(int key)
[ODataRoute("Employees({key})/UnTypedFriends")]
public ITestActionResult GetUnTypedFriends(int key)
{
- var entity = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkInsert.UnTypedEmployee") as IEdmEntityType;
+ var entity = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkOperation.UnTypedEmployee") as IEdmEntityType;
InitTypeLessEmployees(entity);
foreach (var emp in EmployeesTypeless)
@@ -236,7 +232,6 @@ public ITestActionResult GetUnTypedFriends(int key)
return Ok();
}
-
[ODataRoute("Employees")]
[HttpPatch]
[EnableQuery]
@@ -251,6 +246,18 @@ public ITestActionResult PatchEmployees([FromBody] DeltaSet coll)
return Ok(returncoll);
}
+ [ODataRoute("Employees")]
+ [HttpPost]
+ public ITestActionResult Post([FromBody] Employee employee)
+ {
+ InitEmployees();
+
+ var handler = new EmployeeAPIHandler();
+
+ handler.UpdateLinkedObjects(employee, Request.GetModel());
+
+ return Ok(employee);
+ }
[ODataRoute("Employees({key})/Friends")]
[HttpPatch]
@@ -266,7 +273,6 @@ public ITestActionResult PatchFriends(int key, [FromBody] DeltaSet frien
return Ok(changedObjColl);
}
-
[ODataRoute("Employees({key})/NewFriends")]
[HttpPatch]
public ITestActionResult PatchNewFriends(int key, [FromBody] DeltaSet friendColl)
@@ -289,7 +295,6 @@ public ITestActionResult PatchNewFriends(int key, [FromBody] DeltaSet
return Ok(changedObjColl);
}
-
}
[ODataRoute("Employees({key})/UnTypedFriends")]
@@ -316,7 +321,6 @@ public ITestActionResult PatchUnTypedFriends(int key, [FromBody] EdmChangedObjec
(obj1 as EdmStructuredObject).TryGetPropertyValue("Street", out obj2);
Assert.Equal("Abc 123", obj2);
-
}
}
@@ -324,7 +328,7 @@ public ITestActionResult PatchUnTypedFriends(int key, [FromBody] EdmChangedObjec
}
else if (key == 2)
{
- var entitytype = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkInsert.UnTypedEmployee") as IEdmEntityType;
+ var entitytype = Request.GetModel().FindDeclaredType("Microsoft.Test.E2E.AspNet.OData.BulkOperation.UnTypedEmployee") as IEdmEntityType;
var entity = new EdmEntityObject(friendColl[0].GetEdmType().AsEntity());
entity.TrySetPropertyValue("Id", 2);
@@ -348,20 +352,15 @@ public ITestActionResult PatchUnTypedFriends(int key, [FromBody] EdmChangedObjec
}
}
-
[ODataRoute("UnTypedEmployees")]
[HttpPatch]
public ITestActionResult PatchUnTypedEmployees([FromBody] EdmChangedObjectCollection empColl)
{
-
var changedObjColl = EmployeePatchMethodTypeLess(empColl);
return Ok(changedObjColl);
-
}
-
-
[ODataRoute("Employees({key})")]
[EnableQuery]
public ITestActionResult Patch(int key, [FromBody] Delta delta)
@@ -396,8 +395,6 @@ public ITestActionResult Patch(int key, [FromBody] Delta delta)
return Ok(employee);
}
-
-
}
public class CompanyController : TestODataController
@@ -416,30 +413,28 @@ public CompanyController()
private void InitCompanies()
{
- OverdueOrders = new List() { new NewOrder { Id = 1, Price = 10, Quantity =1 }, new NewOrder { Id = 2, Price = 20, Quantity = 2 }, new NewOrder { Id = 3, Price = 30 }, new NewOrder { Id = 4, Price = 40 } };
+ OverdueOrders = new List() { new NewOrder { Id = 1, Price = 10, Quantity = 1 }, new NewOrder { Id = 2, Price = 20, Quantity = 2 }, new NewOrder { Id = 3, Price = 30 }, new NewOrder { Id = 4, Price = 40 } };
MyOverdueOrders = new List() { new MyNewOrder { Id = 1, Price = 10, Quantity = 1 }, new MyNewOrder { Id = 2, Price = 20, Quantity = 2 }, new MyNewOrder { Id = 3, Price = 30 }, new MyNewOrder { Id = 4, Price = 40 } };
Companies = new List() { new Company { Id = 1, Name = "Company1", OverdueOrders = OverdueOrders.Where(x => x.Id == 2).ToList(), MyOverdueOrders = MyOverdueOrders.Where(x => x.Id == 2).ToList() } ,
new Company { Id = 2, Name = "Company2", OverdueOrders = OverdueOrders.Where(x => x.Id == 3 || x.Id == 4).ToList() } };
}
-
[ODataRoute("Companies")]
[HttpPatch]
public ITestActionResult PatchCompanies([FromBody] DeltaSet coll)
{
- var empCntrl = new EmployeesController();
InitCompanies();
Assert.NotNull(coll);
-
+
var returncoll = coll.Patch(new CompanyAPIHandler(), new APIHandlerFactory(Request.GetModel()));
var comp = coll.First() as Delta;
object val;
- if(comp.TryGetPropertyValue("Name", out val))
+ if (comp.TryGetPropertyValue("Name", out val))
{
- if(val.ToString() == "Company02")
+ if (val.ToString() == "Company02")
{
ValidateOverdueOrders2(1, 2, 9);
}
@@ -449,7 +444,6 @@ public ITestActionResult PatchCompanies([FromBody] DeltaSet coll)
}
}
-
return Ok(returncoll);
}
@@ -457,17 +451,18 @@ public ITestActionResult PatchCompanies([FromBody] DeltaSet coll)
[HttpPost]
public ITestActionResult Post([FromBody] Company company)
{
-
+
InitCompanies();
InitEmployees();
- if(company.Id == 4)
+ if (company.Id == 4)
{
AddNewOrder(company);
}
- var idResolver = new BulkOpODataIdResolver(Request.GetModel());
- idResolver.ApplyODataId(company );
+ var handler = new CompanyAPIHandler();
+
+ handler.UpdateLinkedObjects(company, Request.GetModel());
Companies.Add(company);
@@ -479,7 +474,6 @@ public ITestActionResult Post([FromBody] Company company)
{
ValidateOverdueOrders1(3, 1);
}
-
return Ok(company);
}
@@ -491,167 +485,12 @@ private static void AddNewOrder(Company company)
company.OverdueOrders[1] = newOrder;
}
-
- private void MapOdataId(Company company)
- {
- //More generic.
- for(int i =0; i< company.OverdueOrders.Count;i++)
- {
- var order = company.OverdueOrders[i];
- if(order.Container != null)
- {
- var pathItems = new NavigationPath(order.Container.ODataId, Request.GetModel());
-
- int cnt = 0;
- if(pathItems[cnt].Name== "Employees")
- {
- var emp = GetEmployee(pathItems[cnt].KeyProperties);
-
- if(emp != null)
- {
- if(pathItems[++cnt].Name == "NewFriends")
- {
- var frnd = GetNewFriendFromEmployee(emp, pathItems[cnt].KeyProperties);
-
- if(frnd!= null)
- {
- if (pathItems[++cnt].Name == "NewOrders")
- {
- //{ ID= 1, OdataIdContainer {....}} - add comments.
- company.OverdueOrders[i] = GetNewOrderFromNewFriend(frnd, pathItems[cnt].KeyProperties);
- }
- }
- }
- }
- }
-
- }
- }
- }
-
-
-
- private void CheckAndApplyODataId(object obj)
- {
- Type type = obj.GetType();
-
- PropertyInfo property = type.GetProperties().FirstOrDefault(s => s.PropertyType == typeof(IODataIdContainer));
-
- if(property != null && property.GetValue(obj) is IODataIdContainer container && container != null)
- {
- var res = ApplyODataId(container);
-
- foreach(var prop in type.GetProperties())
- {
- var resVal = prop.GetValue(res);
-
-
- if(resVal != null)
- {
- prop.SetValue(obj, resVal);
- }
- }
- }
- else
- {
- foreach (var prop in type.GetProperties().Where(p=> !p.PropertyType.IsPrimitive ))
- {
- var propVal = prop.GetValue(obj);
- if(propVal == null)
- {
- continue;
- }
-
- if (propVal is IEnumerable lst)
- {
-
- foreach (var val in lst)
- {
- if (val.GetType().IsPrimitive)
- {
- break;
- }
-
- CheckAndApplyODataId(val);
-
- }
- }
- else
- {
- CheckAndApplyODataId(propVal);
- }
-
- }
- }
-
- }
-
- private object ApplyODataId(IODataIdContainer container)
- {
- var pathItems = new NavigationPath (container.ODataId, Request.GetModel());
- if(pathItems != null)
- {
- int cnt = 0;
- object value = null;
-
- while(cnt< pathItems.Count)
- {
- value = GetObject(pathItems[cnt].Name, value, pathItems[cnt].KeyProperties);
- cnt++;
- }
-
- return value;
- }
-
- return null;
- }
-
- private object GetObject(string name, object parent, Dictionary keyValues)
- {
- switch (name)
- {
- case "Employees":
- return EmployeesController.Employees.FirstOrDefault(x => x.ID == (int)keyValues["ID"]);
- case "NewFriends":
- Employee emp = parent as Employee;
-
- return emp == null? null: emp.NewFriends.FirstOrDefault(x => x.Id == (int)keyValues["Id"]);
- case "NewOrders":
- NewFriend frnd = parent as NewFriend;
-
- return frnd == null ? null : frnd.NewOrders.FirstOrDefault(x => x.Id == (int)keyValues["Id"]);
- default:
- return null;
- }
- }
-
- private Employee GetEmployee(Dictionary keyValues)
- {
- var emp = EmployeesController.Employees.FirstOrDefault(x => x.ID == (int)keyValues["ID"]);
-
- return emp;
- }
-
- private NewFriend GetNewFriendFromEmployee(Employee emp, Dictionary keyValues)
- {
- var frnd = emp.NewFriends.FirstOrDefault(x => x.Id == (int)keyValues["Id"]);
-
- return frnd;
- }
-
- private NewOrder GetNewOrderFromNewFriend(NewFriend frnd, Dictionary keyValues)
- {
- var order = frnd.NewOrders.FirstOrDefault(x => x.Id == (int)keyValues["Id"]);
-
- return order;
- }
-
private void InitEmployees()
{
var cntrl = new EmployeesController();
}
- private void ValidateOverdueOrders1(int companyId, int orderId, int quantity = 0, int price=101)
+ private void ValidateOverdueOrders1(int companyId, int orderId, int quantity = 0, int price = 101)
{
var comp = Companies.FirstOrDefault(x => x.Id == companyId);
Assert.NotNull(comp);
@@ -675,33 +514,4 @@ private void ValidateOverdueOrders2(int companyId, int orderId, int quantity = 0
Assert.Equal(quantity, order.Quantity);
}
}
-
- internal class BulkOpODataIdResolver: ODataIDResolver
- {
-
- public BulkOpODataIdResolver(IEdmModel model): base(model)
- {
-
- }
-
- public override object GetObject(string name, object parent, Dictionary keyValues)
- {
- switch (name)
- {
- case "Employees":
- return EmployeesController.Employees.FirstOrDefault(x => x.ID == (int)keyValues["ID"]);
- case "NewFriends":
- Employee emp = parent as Employee;
-
- return emp == null ? null : emp.NewFriends.FirstOrDefault(x => x.Id == (int)keyValues["Id"]);
- case "NewOrders":
- NewFriend frnd = parent as NewFriend;
-
- return frnd == null ? null : frnd.NewOrders.FirstOrDefault(x => x.Id == (int)keyValues["Id"]);
- default:
- return null;
- }
- }
-
- }
}
\ No newline at end of file
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationDataModel.cs
similarity index 97%
rename from test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs
rename to test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationDataModel.cs
index fcf3c09214..a2f8f1072d 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertDataModel.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationDataModel.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
-//
+//
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
//
@@ -12,7 +12,7 @@
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Builder;
-namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
+namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
[AutoExpand]
public class Employee
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertEdmModel.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationEdmModel.cs
similarity index 96%
rename from test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertEdmModel.cs
rename to test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationEdmModel.cs
index f69e877c6b..0ccc5aef7e 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertEdmModel.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationEdmModel.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
-//
+//
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
//
@@ -9,9 +9,9 @@
using Microsoft.OData.Edm;
using Microsoft.Test.E2E.AspNet.OData.Common.Execution;
-namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
+namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
- internal class BulkInsertEdmModel
+ internal class BulkOperationEdmModel
{
public static IEdmModel GetExplicitModel(WebRouteConfiguration configuration)
{
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationPatchHandlers.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationPatchHandlers.cs
index a4344344dc..ea6924bad7 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationPatchHandlers.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationPatchHandlers.cs
@@ -9,107 +9,148 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.OData;
+using Microsoft.AspNet.OData.Common;
+using Microsoft.AspNet.OData.Extensions;
using Microsoft.OData.Edm;
-using Microsoft.Test.E2E.AspNet.OData.BulkInsert;
+using Microsoft.OData.UriParser;
namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
internal class APIHandlerFactory : ODataAPIHandlerFactory
{
- public APIHandlerFactory(IEdmModel model) : base (model)
+ public APIHandlerFactory(IEdmModel model) : base(model)
{
}
- public override IODataAPIHandler GetHandler(NavigationPath navigationPath)
+ public override IODataAPIHandler GetHandler(ODataPath odataPath)
{
- if(navigationPath != null)
+ if (odataPath != null)
{
- var pathItems = navigationPath;
+ int currentPosition = 0;
- int cnt = 0;
-
- switch (pathItems[cnt].Name)
- {
- case "Employees":
- {
- Employee employee;
- string msg;
- if ((new EmployeeAPIHandler().TryGet(pathItems[cnt].KeyProperties, out employee, out msg)) == ODataAPIResponseStatus.Success)
- {
- return GetNestedHandlerForEmployee(pathItems, cnt, employee);
- }
- }
- return null;
- case "Companies":
- return new CompanyAPIHandler();
+ if (odataPath.Count == 1)
+ {
+ GetHandlerInternal(odataPath.FirstSegment.Identifier, currentPosition);
+ }
- default:
- return null;
+ List pathSegments = odataPath.GetSegments();
- }
-
+ ODataPathSegment currentPathSegment = pathSegments[currentPosition];
+
+ if (currentPathSegment is EntitySetSegment || currentPathSegment is NavigationPropertySegment || currentPathSegment is SingletonSegment)
+ {
+ int keySegmentPosition = ODataPathHelper.GetNextKeySegmentPosition(pathSegments, currentPosition);
+ KeySegment keySegment = (KeySegment)pathSegments[keySegmentPosition];
+
+ currentPosition = keySegmentPosition;
+
+ return GetHandlerInternal(
+ currentPathSegment.Identifier,
+ currentPosition,
+ ODataPathHelper.KeySegmentAsDictionary(keySegment),
+ pathSegments);
+ }
}
return null;
}
- private static IODataAPIHandler GetNestedHandlerForEmployee(List pathItems, int cnt, Employee employee)
+ private IODataAPIHandler GetHandlerInternal(
+ string pathName,
+ int currentPosition,
+ Dictionary keys = null,
+ List pathSegments = null)
{
- cnt++;
- if(pathItems.Count <= cnt)
+ switch (pathName)
+ {
+ case "Employees":
+ Employee employee;
+ string msg;
+ if ((new EmployeeAPIHandler().TryGet(keys, out employee, out msg)) == ODataAPIResponseStatus.Success)
+ {
+ return GetNestedHandlerForEmployee(pathSegments, currentPosition, employee);
+ }
+ return null;
+ case "Companies":
+ return new CompanyAPIHandler();
+
+ default:
+ return null;
+ }
+ }
+
+ private static IODataAPIHandler GetNestedHandlerForEmployee(List pathSegments, int currentPosition, Employee employee)
+ {
+ ++currentPosition;
+
+ if (pathSegments.Count <= currentPosition)
{
return null;
}
- switch (pathItems[cnt].Name)
+ ODataPathSegment currentPathSegment = pathSegments[currentPosition];
+
+ if (currentPathSegment is NavigationPropertySegment)
{
- case "NewFriends":
- CastTypePathItem castItem = pathItems[cnt] as CastTypePathItem;
+ int keySegmentPosition = ODataPathHelper.GetNextKeySegmentPosition(pathSegments, currentPosition);
+ KeySegment keySegment = (KeySegment)pathSegments[keySegmentPosition];
+ Dictionary keys = ODataPathHelper.KeySegmentAsDictionary(keySegment);
- if (castItem != null)
- {
- if (castItem.CastTypeName == "Microsoft.Test.E2E.AspNet.OData.BulkInsert.MyNewFriend")
+ currentPosition = keySegmentPosition;
+
+ switch (currentPathSegment.Identifier)
+ {
+ case "NewFriends":
+ ODataPathSegment nextPathSegment = pathSegments[++currentPosition];
+
+ if (nextPathSegment is TypeSegment)
{
- MyNewFriend friend = employee.NewFriends.FirstOrDefault(x => x.Id == (int)pathItems[cnt].KeyProperties["Id"]) as MyNewFriend;
+ currentPosition++;
+ TypeSegment typeSegment = nextPathSegment as TypeSegment;
- if (friend != null)
+ if (typeSegment.Identifier == "Microsoft.Test.E2E.AspNet.OData.BulkOperation.MyNewFriend")
{
- switch (pathItems[++cnt].Name)
+ MyNewFriend friend = employee.NewFriends.FirstOrDefault(x => x.Id == (int)keys["Id"]) as MyNewFriend;
+
+ if (friend != null)
{
- case "MyNewOrders":
- return new MyNewOrderAPIHandler(friend);
+ switch (pathSegments[++currentPosition].Identifier)
+ {
+ case "MyNewOrders":
+ return new MyNewOrderAPIHandler(friend);
- default:
- return null;
+ default:
+ return null;
+ }
}
}
}
- }
- else
- {
- NewFriend friend = employee.NewFriends.FirstOrDefault(x => x.Id == (int)pathItems[cnt].KeyProperties["Id"]);
-
- if (friend != null)
+ else
{
- switch (pathItems[++cnt].Name)
+ NewFriend friend = employee.NewFriends.FirstOrDefault(x => x.Id == (int)keys["Id"]);
+
+ if (friend != null)
{
- case "NewOrders":
- return new NewOrderAPIHandler(friend);
+ switch (pathSegments[++currentPosition].Identifier)
+ {
+ case "NewOrders":
+ return new NewOrderAPIHandler(friend);
- default:
- return null;
+ default:
+ return null;
+ }
}
}
- }
-
- return null;
+ return null;
- default:
- return null;
+ default:
+ return null;
+ }
}
+ return null;
}
}
@@ -122,95 +163,25 @@ public TypelessAPIHandlerFactory(IEdmModel model, IEdmEntityType entityType) : b
this.entityType = entityType;
}
- public override EdmODataAPIHandler GetHandler(NavigationPath navigationPath)
+ public override EdmODataAPIHandler GetHandler(ODataPath odataPath)
{
- if (navigationPath != null)
+ if (odataPath != null)
{
- var pathItems = navigationPath;
- int cnt = 0;
+ string pathName = odataPath.GetLastNonTypeNonKeySegment().Identifier;
- switch (pathItems[cnt].Name)
+ switch (pathName)
{
case "UnTypedEmployees":
- {
- IEdmStructuredObject employee;
- string msg;
- if ((new EmployeeEdmAPIHandler(entityType).TryGet(pathItems[cnt].KeyProperties, out employee, out msg)) == ODataAPIResponseStatus.Success)
- {
- cnt++;
-
- if (cnt x.Id == (int)pathItems[cnt].KeyProperties["Id"]) as MyNewFriend;
-
- if (friend != null)
- {
- switch (pathItems[++cnt].Name)
- {
- case "MyNewOrders":
- return new MyNewOrderAPIHandler(friend);
-
- default:
- return null;
-
- }
- }
- }
- }
- else
- {
- NewFriend friend = employee.NewFriends.FirstOrDefault(x => x.Id == (int)pathItems[cnt].KeyProperties["Id"]);
-
- if (friend != null)
- {
- switch (pathItems[++cnt].Name)
- {
- case "NewOrders":
- return new NewOrderAPIHandler(friend);
-
- default:
- return null;
-
- }
- }
- }
-
- return null;
-
- default:
- return null;
-
- }
- }
}
@@ -268,7 +239,6 @@ public override ODataAPIResponseStatus TryGet(IDictionary keyVal
var id = keyValues["Id"].ToString();
originalObject = CompanyController.Companies.First(x => x.Id == Int32.Parse(id));
-
if (originalObject == null)
{
status = ODataAPIResponseStatus.NotFound;
@@ -480,11 +450,10 @@ public override IODataAPIHandler GetNestedHandler(MyNewOrder parent, string navi
internal class EmployeeAPIHandler : ODataAPIHandler
{
-
public override ODataAPIResponseStatus TryCreate(IDictionary keyValues, out Employee createdObject, out string errorMessage)
{
createdObject = null;
- errorMessage = string.Empty;
+ errorMessage = null;
try
{
@@ -503,7 +472,7 @@ public override ODataAPIResponseStatus TryCreate(IDictionary key
public override ODataAPIResponseStatus TryDelete(IDictionary keyValues, out string errorMessage)
{
- errorMessage = string.Empty;
+ errorMessage = null;
try
{
@@ -525,7 +494,7 @@ public override ODataAPIResponseStatus TryDelete(IDictionary key
public override ODataAPIResponseStatus TryGet(IDictionary keyValues, out Employee originalObject, out string errorMessage)
{
ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
- errorMessage = string.Empty;
+ errorMessage = null;
originalObject = null;
try
@@ -560,7 +529,7 @@ public override IODataAPIHandler GetNestedHandler(Employee parent, string naviga
default:
return null;
}
-
+
}
}
@@ -853,8 +822,8 @@ public override ODataAPIResponseStatus TryCreate(IDictionary key
try
{
createdObject = new Order();
-
- if(friend.Orders == null)
+
+ if (friend.Orders == null)
{
friend.Orders = new List();
}
@@ -947,7 +916,7 @@ public override ODataAPIResponseStatus TryCreate(IDictionary key
{
createdObject = new NewFriend();
- if(employee.NewFriends == null)
+ if (employee.NewFriends == null)
{
employee.NewFriends = new List();
}
@@ -995,7 +964,7 @@ public override ODataAPIResponseStatus TryGet(IDictionary keyVal
{
var id = keyValues["Id"].ToString();
- if(employee.NewFriends == null)
+ if (employee.NewFriends == null)
{
return ODataAPIResponseStatus.NotFound;
}
@@ -1072,7 +1041,7 @@ public override ODataAPIResponseStatus TryDelete(IDictionary key
break;
}
}
-
+
return ODataAPIResponseStatus.Success;
}
@@ -1093,17 +1062,17 @@ public override ODataAPIResponseStatus TryGet(IDictionary keyVal
try
{
var id = keyValues["ID"].ToString();
- foreach (var emp in EmployeesController.EmployeesTypeless)
- {
+ foreach (var emp in EmployeesController.EmployeesTypeless)
+ {
object id1;
emp.TryGetPropertyValue("ID", out id1);
- if(id == id1.ToString())
+ if (id == id1.ToString())
{
originalObject = emp;
break;
}
- }
+ }
if (originalObject == null)
@@ -1127,11 +1096,11 @@ public override EdmODataAPIHandler GetNestedHandler(IEdmStructuredObject parent,
{
case "UnTypedFriends":
return new FriendTypelessAPIHandler(parent, entityType.DeclaredNavigationProperties().First().Type.Definition.AsElementType() as IEdmEntityType);
-
+
default:
return null;
}
-
+
}
}
@@ -1141,7 +1110,7 @@ internal class FriendTypelessAPIHandler : EdmODataAPIHandler
IEdmEntityType entityType;
EdmStructuredObject employee;
- public FriendTypelessAPIHandler(IEdmStructuredObject employee, IEdmEntityType entityType)
+ public FriendTypelessAPIHandler(IEdmStructuredObject employee, IEdmEntityType entityType)
{
this.employee = employee as EdmStructuredObject;
this.entityType = entityType;
@@ -1155,7 +1124,7 @@ public override ODataAPIResponseStatus TryCreate(IEdmChangedObject changedObject
try
{
object empid;
- if(employee.TryGetPropertyValue("ID" , out empid) && empid as int? == 3)
+ if (employee.TryGetPropertyValue("ID", out empid) && empid as int? == 3)
{
throw new Exception("Testing Error");
}
@@ -1166,7 +1135,7 @@ public override ODataAPIResponseStatus TryCreate(IEdmChangedObject changedObject
var friends = obj as ICollection;
- if(friends == null)
+ if (friends == null)
{
friends = new List();
}
@@ -1192,7 +1161,7 @@ public override ODataAPIResponseStatus TryDelete(IDictionary key
try
{
var id = keyValues.First().Value.ToString();
- if(id == "5")
+ if (id == "5")
{
throw new Exception("Testing Error");
}
@@ -1211,7 +1180,7 @@ public override ODataAPIResponseStatus TryDelete(IDictionary key
friends.Remove(emp);
employee.TrySetPropertyValue("UnTypedFriends", friends);
-
+
break;
}
}
@@ -1241,7 +1210,7 @@ public override ODataAPIResponseStatus TryGet(IDictionary keyVal
var friends = obj as IList;
- if(friends == null)
+ if (friends == null)
{
return ODataAPIResponseStatus.NotFound;
}
@@ -1276,7 +1245,7 @@ public override ODataAPIResponseStatus TryGet(IDictionary keyVal
public override EdmODataAPIHandler GetNestedHandler(IEdmStructuredObject parent, string navigationPropertyName)
{
- return null;
+ return null;
}
}
diff --git a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationTest.cs
similarity index 94%
rename from test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs
rename to test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationTest.cs
index bce4ce02fd..43e5677536 100644
--- a/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkInsertTest.cs
+++ b/test/E2ETest/Microsoft.Test.E2E.AspNet.OData/BulkOperation/BulkOperationTest.cs
@@ -1,11 +1,10 @@
//-----------------------------------------------------------------------------
-//
+//
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
//
//------------------------------------------------------------------------------
-using System;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -16,19 +15,16 @@
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNet.OData.Routing.Conventions;
-using Microsoft.OData;
-using Microsoft.OData.Edm;
using Microsoft.Test.E2E.AspNet.OData.Common.Execution;
using Microsoft.Test.E2E.AspNet.OData.Common.Extensions;
-using Microsoft.Test.E2E.AspNet.OData.ModelBuilder;
using Newtonsoft.Json.Linq;
using Xunit;
-namespace Microsoft.Test.E2E.AspNet.OData.BulkInsert
+namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
- public class BulkInsertTest : WebHostTestBase
+ public class BulkOperationTest : WebHostTestBase
{
- public BulkInsertTest(WebHostTestFixture fixture)
+ public BulkOperationTest(WebHostTestFixture fixture)
:base(fixture)
{
}
@@ -40,8 +36,8 @@ protected override void UpdateConfiguration(WebRouteConfiguration configuration)
configuration.Routes.Clear();
configuration.Count().Filter().OrderBy().Expand().MaxTop(null).Select();
- configuration.MapODataServiceRoute("convention", "convention", BulkInsertEdmModel.GetConventionModel(configuration));
- configuration.MapODataServiceRoute("explicit", "explicit", BulkInsertEdmModel.GetExplicitModel(configuration), new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault());
+ configuration.MapODataServiceRoute("convention", "convention", BulkOperationEdmModel.GetConventionModel(configuration));
+ configuration.MapODataServiceRoute("explicit", "explicit", BulkOperationEdmModel.GetExplicitModel(configuration), new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault());
configuration.EnsureInitialized();
}
@@ -60,11 +56,6 @@ public async Task PatchEmployee_WithUpdates()
'Friends@odata.delta':[{'Id':1,'Name':'Test2'},{'Id':2,'Name':'Test3'}]
}";
- //content = @"{
- // 'Name':'Sql' , 'FavoriteSports' :{'Sport': 'Cricket'}
-
- // }";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
@@ -89,7 +80,6 @@ public async Task PatchEmployee_WithUpdates()
Assert.Equal(2, result.Count);
Assert.Contains("Test2", result.ToString());
}
-
}
[Fact]
@@ -129,10 +119,8 @@ public async Task PatchEmployee_WithUpdates_WithEmployees()
Assert.Contains("400", result.ToString());
Assert.Contains("900", result.ToString());
}
-
}
-
[Fact]
public async Task PatchEmployee_WithUpdates_Friends()
{
@@ -140,7 +128,7 @@ public async Task PatchEmployee_WithUpdates_Friends()
string requestUri = this.BaseAddress + "/convention/Employees(1)/Friends";
- var content = @"{'@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Friend',
+ var content = @"{'@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Friend',
'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/Friends/$delta',
'value':[{ 'Id':1,'Name':'Friend1'}, { 'Id':2,'Name':'Friend2'}]
}";
@@ -216,7 +204,6 @@ public async Task PatchEmployee_WithDeletes_Friends()
Assert.Single(result);
Assert.Contains("Friend2", result.ToString());
}
-
}
[Fact]
@@ -226,11 +213,10 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedTypes()
string requestUri = this.BaseAddress + "/convention/Employees(1)/Friends";
- var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/Friends/$delta', '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Friend',
+ var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/Friends/$delta', '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Friend',
'value':[{ '@odata.removed' : {'reason':'changed'}, 'Id':1, 'Orders@odata.delta' :[{'Id':1,'Price': 10}, {'Id':2,'Price': 20} ] },{ 'Id':2,'Name':'Friend2'}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -259,7 +245,6 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedTypes()
Assert.Single(result);
Assert.Contains("Friend2", result.ToString());
}
-
}
[Fact]
@@ -269,11 +254,10 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedDeletes()
string requestUri = this.BaseAddress + "/convention/Employees(1)/Friends";
- var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/Friends/$delta', '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Friend',
+ var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/Friends/$delta', '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Friend',
'value':[{ '@odata.removed' : {'reason':'changed'}, 'Id':1, 'Orders@odata.delta' :[{'@odata.removed' : {'reason':'changed'}, 'Id':1,'Price': 10}, {'Id':2,'Price': 20} ] },{ 'Id':2,'Name':'Friend2'}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -290,7 +274,6 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedDeletes()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains(expected.ToLower(), json.ToString().ToLower());
}
-
requestUri = this.BaseAddress + "/convention/Employees(1)/Friends";
using (HttpResponseMessage response = await this.Client.GetAsync(requestUri))
@@ -302,7 +285,6 @@ public async Task PatchEmployee_WithDeletes_Friends_WithNestedDeletes()
Assert.Contains("Friend2", result.ToString());
}
-
}
[Fact]
@@ -311,19 +293,15 @@ public async Task PatchEmployee_WithAdds_Friends_WithAnnotations()
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(1)/NewFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/NewFriends/$delta',
'value':[{ 'Id':3, 'Age':35, '@NS.Test':1}]
}";
-
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");
- // StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
- //requestForPost.Content = stringContent;
Client.DefaultRequestHeaders.Add("Prefer", @"odata.include-annotations=""*""");
@@ -343,11 +321,9 @@ public async Task PatchEmployee_WithFailedAdds_Friends()
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(1)/NewFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/NewFriends/$delta',
'value':[{ 'Id':3, 'Age':3, '@NS.Test':1}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -371,12 +347,10 @@ public async Task PatchEmployee_WithFailedDeletes_Friends()
{
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(2)/NewFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/NewFriends/$delta',
'value':[{ '@odata.removed' : {'reason':'changed'}, 'Id':2, '@NS.Test':1}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -394,21 +368,17 @@ public async Task PatchEmployee_WithFailedDeletes_Friends()
Assert.Contains("$delta", json);
Assert.Contains(expected, json.ToString());
}
-
}
-
[Fact]
public async Task PatchEmployee_WithFailedOperation_WithAnnotations()
{
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(2)/NewFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(2)/NewFriends/$delta',
'value':[{ '@odata.removed' : {'reason':'changed'}, 'Id':2, '@Core.ContentID':3, '@NS.Test2':'testing'}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -419,7 +389,6 @@ public async Task PatchEmployee_WithFailedOperation_WithAnnotations()
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;
@@ -430,17 +399,14 @@ public async Task PatchEmployee_WithFailedOperation_WithAnnotations()
Assert.Contains("Core.DataModificationException", str);
Assert.Contains(expected, str);
}
-
}
-
[Fact]
public async Task PatchUntypedEmployee_WithAdds_Friends_Untyped()
{
//Arrange
string requestUri = this.BaseAddress + "/convention/UnTypedEmployees";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(2)/UnTypedFriends/$delta',
'value':[{ 'Id':3, 'Age':35,}]
}";
@@ -454,7 +420,6 @@ public async Task PatchUntypedEmployee_WithAdds_Friends_Untyped()
}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -466,7 +431,6 @@ public async Task PatchUntypedEmployee_WithAdds_Friends_Untyped()
"[{\"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;
@@ -475,19 +439,16 @@ public async Task PatchUntypedEmployee_WithAdds_Friends_Untyped()
}
}
-
[Fact]
public async Task PatchEmployee_WithAdds_Friends_WithNested_Untyped()
{
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(1)/UnTypedFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(1)/UnTypedFriends/$delta',
'value':[{ 'Id':2, 'Name': 'Friend007', 'Age':35,'Address@odata.delta':{'Id':1, 'Street' : 'Abc 123'}, '@NS.Test':1}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
@@ -501,22 +462,18 @@ public async Task PatchEmployee_WithAdds_Friends_WithNested_Untyped()
json.ToString().Contains("$delta");
json.ToString().Contains("@NS.Test");
}
-
}
-
[Fact]
public async Task PatchEmployee_WithAdds_Friends_WithAnnotations_Untyped()
{
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(2)/UnTypedFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(2)/UnTypedFriends/$delta',
'value':[{ 'Id':2, 'Age':35, '@NS.Test':1}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
@@ -538,12 +495,10 @@ public async Task PatchEmployee_WithFailedAdds_Friends_Untyped()
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(3)/UnTypedFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(3)/UnTypedFriends/$delta',
'value':[{ 'Id':3, 'Age':3, '@NS.Test':1}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
@@ -568,7 +523,6 @@ public async Task PatchEmployee_WithFailedDeletes_Friends_Untyped()
'value':[{ '@odata.removed' : {'reason':'changed'}, 'Id':5, '@NS.Test':1}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
@@ -582,21 +536,17 @@ public async Task PatchEmployee_WithFailedDeletes_Friends_Untyped()
Assert.Contains("@Core.DataModificationException", json.ToString());
Assert.Contains("@NS.Test", json.ToString());
}
-
}
-
[Fact]
public async Task PatchEmployee_WithFailedOperation_WithAnnotations_Untyped()
{
//Arrange
string requestUri = this.BaseAddress + "/convention/Employees(3)/UnTypedFriends";
- //{ '@odata.removed' : {'reason':'changed'}, 'Id':1},{ '@odata.removed' : {'reason':'deleted'}, 'Id':2},
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Employees(3)/UnTypedFriends/$delta',
'value':[{ '@odata.removed' : {'reason':'changed'}, 'Id':5, '@Core.ContentID':3, '@NS.Test2':'testing'}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
@@ -613,7 +563,6 @@ public async Task PatchEmployee_WithFailedOperation_WithAnnotations_Untyped()
Assert.Contains("Core.DataModificationException", str);
Assert.Contains("Core.ContentID", str);
}
-
}
[Fact]
@@ -624,11 +573,11 @@ public async Task PatchEmployee_WithUpdates_Employees()
string requestUri = this.BaseAddress + "/convention/Employees";
var content = @"{'@odata.context':'"+ this.BaseAddress + @"/convention/$metadata#Employees/$delta',
- 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Employee', 'ID':1,'Name':'Employee1',
+ 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Employee', 'ID':1,'Name':'Employee1',
'Friends@odata.delta':[{'Id':1,'Name':'Friend1',
'Orders@odata.delta' :[{'Id':1,'Price': 10}, {'Id':2,'Price': 20} ] },{'Id':2,'Name':'Friend2'}]
},
- { '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Employee', 'ID':2,'Name':'Employee2',
+ { '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Employee', 'ID':2,'Name':'Employee2',
'Friends@odata.delta':[{'Id':3,'Name':'Friend3',
'Orders@odata.delta' :[{'Id':3,'Price': 30}, {'Id':4,'Price': 40} ]},{'Id':4,'Name':'Friend4'}]
}]
@@ -676,7 +625,6 @@ public async Task PatchEmployee_WithUpdates_Employees()
}
}
-
[Fact]
public async Task PatchEmployee_WithDelete()
{
@@ -749,7 +697,6 @@ public async Task PatchEmployee_WithODataBind()
}
}
-
[Fact]
public async Task PatchEmployee_WithAddUpdateAndDelete()
{
@@ -789,7 +736,6 @@ public async Task PatchEmployee_WithAddUpdateAndDelete()
}
}
-
[Fact]
public async Task PatchEmployee_WithMultipleUpdatesinOrder1()
{
@@ -870,7 +816,6 @@ public async Task PatchEmployee_WithMultipleUpdatesinOrder2()
}
}
-
[Fact]
public async Task PatchCompanies_WithUpdates_ODataId()
{
@@ -879,13 +824,11 @@ public async Task PatchCompanies_WithUpdates_ODataId()
string requestUri = this.BaseAddress + "/convention/Companies";
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Companies/$delta',
- 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Company', 'Id':1,'Name':'Company01',
+ 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Company', 'Id':1,'Name':'Company01',
'OverdueOrders@odata.delta':[{'@odata.id':'Employees(1)/NewFriends(1)/NewOrders(1)', 'Quantity': 9}]
-
}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -902,8 +845,6 @@ public async Task PatchCompanies_WithUpdates_ODataId()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains(expected, json.ToString());
}
-
-
}
[Fact]
@@ -914,13 +855,11 @@ public async Task PatchCompanies_WithUpdates_ODataId_WithCast()
string requestUri = this.BaseAddress + "/convention/Companies";
var content = @"{'@odata.context':'" + this.BaseAddress + @"/convention/$metadata#Companies/$delta',
- 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkInsert.Company', 'Id':1,'Name':'Company02',
- 'MyOverdueOrders@odata.delta':[{'@odata.id':'Employees(2)/NewFriends(2)/Microsoft.Test.E2E.AspNet.OData.BulkInsert.MyNewFriend/MyNewOrders(2)', 'Quantity': 9}]
-
+ 'value':[{ '@odata.type': '#Microsoft.Test.E2E.AspNet.OData.BulkOperation.Company', 'Id':1,'Name':'Company02',
+ 'MyOverdueOrders@odata.delta':[{'@odata.id':'Employees(2)/NewFriends(2)/Microsoft.Test.E2E.AspNet.OData.BulkOperation.MyNewFriend/MyNewOrders(2)', 'Quantity': 9}]
}]
}";
-
var requestForPost = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
requestForPost.Headers.Add("OData-Version", "4.01");
@@ -937,11 +876,8 @@ public async Task PatchCompanies_WithUpdates_ODataId_WithCast()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Contains(expected, json.ToString());
}
-
-
}
-
[Fact]
public async Task PatchUntypedEmployee_WithOdataId()
{
@@ -974,6 +910,9 @@ public async Task PatchUntypedEmployee_WithOdataId()
}
}
+ #endregion
+
+ #region Post
[Fact]
public async Task PostCompany_WithODataId()
{
@@ -983,8 +922,6 @@ public async Task PostCompany_WithODataId()
var content = @"{'Id':3,'Name':'Company03',
'OverdueOrders':[{'@odata.id':'Employees(1)/NewFriends(1)/NewOrders(1)'}]
-
-
}";
var requestForPost = new HttpRequestMessage(new HttpMethod("POST"), requestUri);
@@ -998,10 +935,8 @@ public async Task PostCompany_WithODataId()
var json = response.Content.ReadAsStringAsync().Result;
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
-
}
-
[Fact]
public async Task PostCompany_WithODataId_AndWithout()
{
@@ -1011,8 +946,6 @@ public async Task PostCompany_WithODataId_AndWithout()
var content = @"{'Id':4,'Name':'Company04',
'OverdueOrders':[{'@odata.id':'Employees(1)/NewFriends(1)/NewOrders(1)'},{Price:30}]
-
-
}";
var requestForPost = new HttpRequestMessage(new HttpMethod("POST"), requestUri);
@@ -1026,12 +959,35 @@ public async Task PostCompany_WithODataId_AndWithout()
var json = response.Content.ReadAsStringAsync().Result;
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
-
}
+ [Fact]
+ public async Task PostEmployee_WithCreateFriends()
+ {
+ //Arrange
+ string requestUri = this.BaseAddress + "/convention/Employees";
- #endregion
+ var content = @"{
+ 'Name':'SqlUD',
+ 'Friends':[{ 'Id':1001, 'Name' : 'Friend 1001', 'Age': 31},{ 'Id':1002, 'Name' : 'Friend 1002', 'Age': 32},{ 'Id':1003, 'Name' : 'Friend 1003', 'Age': 33}]
+ }";
+
+ var requestForPatch = new HttpRequestMessage(new HttpMethod("POST"), requestUri);
+
+ StringContent stringContent = new StringContent(content: content, encoding: Encoding.UTF8, mediaType: "application/json");
+ requestForPatch.Content = stringContent;
+ //Act & Assert
+ using (HttpResponseMessage response = await this.Client.SendAsync(requestForPatch))
+ {
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var json = response.Content.ReadAsStringAsync().Result;
+ Assert.Contains("SqlUD", json);
+ //Assert.Contains("Friends", json); // Activate after fixing serialization issue for DeepInsert nested resources
+ }
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems
index acd5930170..48f8deb8fb 100644
--- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems
+++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems
@@ -228,6 +228,8 @@
+
+
@@ -334,6 +336,7 @@
+
diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataPathExtensionsTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataPathExtensionsTest.cs
new file mode 100644
index 0000000000..09b722b044
--- /dev/null
+++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataPathExtensionsTest.cs
@@ -0,0 +1,208 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (c) .NET Foundation and Contributors. All rights reserved.
+// See License.txt in the project root for license information.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNet.OData.Extensions;
+using Microsoft.AspNet.OData.Test.Common.Models;
+using Microsoft.OData.UriParser;
+using Xunit;
+
+namespace Microsoft.AspNet.OData.Test
+{
+ public class ODataPathExtensionsTest
+ {
+ SampleEdmModel model = new SampleEdmModel();
+ KeyValuePair[] customerKey = new[] { new KeyValuePair("Id", "1") };
+ KeyValuePair[] friendsKey = new[] { new KeyValuePair("Id", "1001") };
+
+ [Fact]
+ public void GetKeys_PathWithTypeSegmentReturnsKeysFromLastKeySegment()
+ {
+ // From this path Customers(1)
+ // GetKeys() should return { "Id": "1" }
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new TypeSegment(model.vipCustomerType, model.customerType, null)
+ });
+
+ // Act
+ Dictionary keys = path.GetKeys();
+
+ // Assert
+ Assert.Single(keys);
+ Assert.Equal("Id", keys.First().Key);
+ Assert.Equal("1", keys.First().Value);
+ }
+
+ [Fact]
+ public void GetKeys_PathWithNoSegmentReturnsEmptyCollection()
+ {
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ });
+
+ // Act
+ Dictionary keys = path.GetKeys();
+
+ // Assert
+ Assert.Empty(keys);
+ }
+
+ [Fact]
+ public void GetKeys_PathWithNoKeySegmentReturnsEmptyCollection()
+ {
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new TypeSegment(model.vipCustomerType, model.customerType, null)
+ });
+
+ // Act
+ Dictionary keys = path.GetKeys();
+
+ // Assert
+ Assert.Empty(keys);
+ }
+
+ [Fact]
+ public void GetKeys_PathWithNavPropReturnsKeysFromLastKeySegment()
+ {
+ // From this path Customers(1)/Friends(1001)
+ // GetKeys() should return { "Id": "1001" }
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new NavigationPropertySegment(model.friendsProperty, model.customerSet),
+ new KeySegment(friendsKey, model.personType, null)
+ });
+
+ // Act
+ Dictionary keys = path.GetKeys();
+
+ // Assert
+ Assert.Single(keys);
+ Assert.Equal("Id", keys.First().Key);
+ Assert.Equal("1001", keys.First().Value);
+ }
+
+ [Fact]
+ public void GetLastNonTypeNonKeySegment_TypeSegmentAsLastSegmentReturnsCorrectSegment()
+ {
+ // If the path is Customers(1)/Friends(1001)/Ns.UniquePerson where Ns.UniquePerson is a type segment
+ // and 1001 is a KeySegment,
+ // GetLastNonTypeNonKeySegment() should return Friends NavigationPropertySegment.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new NavigationPropertySegment(model.friendsProperty, model.customerSet),
+ new KeySegment(friendsKey, model.personType, null),
+ new TypeSegment(model.uniquePersonType, model.personType, null)
+ });
+
+ // Act
+ ODataPathSegment segment = path.GetLastNonTypeNonKeySegment();
+
+ // Assert
+ Assert.Equal("Friends", segment.Identifier);
+ Assert.True(segment is NavigationPropertySegment);
+ }
+
+ [Fact]
+ public void GetLastNonTypeNonKeySegment_KeySegmentAsLastSegmentReturnsCorrectSegment()
+ {
+ // If the path is Customers(1)/Friends(1001) where1001 is a KeySegment,
+ // GetLastNonTypeNonKeySegment() should return Friends NavigationPropertySegment.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new NavigationPropertySegment(model.friendsProperty, model.customerSet),
+ new KeySegment(friendsKey, model.personType, null)
+ });
+
+ // Act
+ ODataPathSegment segment = path.GetLastNonTypeNonKeySegment();
+
+ // Assert
+ Assert.Equal("Friends", segment.Identifier);
+ Assert.True(segment is NavigationPropertySegment);
+ }
+
+ [Fact]
+ public void GetLastNonTypeNonKeySegment_SingleSegmentPathReturnsCorrectSegment()
+ {
+ // If the path is /Customers,
+ // GetLastNonTypeNonKeySegment() should return Customers EntitySetSegment.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet)
+ });
+
+ // Act
+ ODataPathSegment segment = path.GetLastNonTypeNonKeySegment();
+
+ // Assert
+ Assert.True(segment is EntitySetSegment);
+ }
+
+ [Fact]
+ public void GetLastNonTypeNonKeySegment_SingleKeySegmentPathReturnsNull()
+ {
+ // If the path is /1,
+ // GetLastNonTypeNonKeySegment() should return null since this is a KeySegment.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new KeySegment(customerKey, model.customerType, model.customerSet)
+ });
+
+ // Act
+ ODataPathSegment segment = path.GetLastNonTypeNonKeySegment();
+
+ // Assert
+ Assert.Null(segment);
+ }
+
+ [Fact]
+ public void GetLastNonTypeNonKeySegment_SingleTypeSegmentPathReturnsNull()
+ {
+ // If the path is /Ns.UniquePerson,
+ // GetLastNonTypeNonKeySegment() should return null since this is a TypeSegment.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new TypeSegment(model.uniquePersonType, model.personType, null)
+ });
+
+ // Act
+ ODataPathSegment segment = path.GetLastNonTypeNonKeySegment();
+
+ // Assert
+ Assert.Null(segment);
+ }
+ }
+}
diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataPathHelperTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataPathHelperTest.cs
new file mode 100644
index 0000000000..d54e622cb4
--- /dev/null
+++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataPathHelperTest.cs
@@ -0,0 +1,118 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (c) .NET Foundation and Contributors. All rights reserved.
+// See License.txt in the project root for license information.
+//
+//------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNet.OData.Common;
+using Microsoft.AspNet.OData.Test.Common;
+using Microsoft.AspNet.OData.Test.Common.Models;
+using Microsoft.OData.UriParser;
+using Xunit;
+
+namespace Microsoft.AspNet.OData.Test
+{
+ public class ODataPathHelperTest
+ {
+ SampleEdmModel model = new SampleEdmModel();
+ KeyValuePair[] customerKey = new[] { new KeyValuePair("Id", "1"), new KeyValuePair("AlternateId", "2") };
+ KeyValuePair[] friendsKey = new[] { new KeyValuePair("Id", "1001") };
+
+ [Fact]
+ public void GetKeysFromKeySegment_ReturnsCorrectKeysDictionary()
+ {
+ // Arrange
+ KeySegment keySegment = new KeySegment(customerKey, model.customerType, model.customerSet);
+
+ // Act
+ Dictionary keys = ODataPathHelper.KeySegmentAsDictionary(keySegment);
+
+ // Assert
+ Assert.Equal(2, keys.Count);
+ Assert.Equal("Id", keys.First().Key);
+ Assert.Equal("1", keys.First().Value);
+ Assert.Equal("AlternateId", keys.Last().Key);
+ Assert.Equal("2", keys.Last().Value);
+ }
+
+ [Fact]
+ public void GetKeysFromKeySegment_ThrowsExceptionForNullKeySegment()
+ {
+ KeySegment keySegment = null;
+
+ ExceptionAssert.ThrowsArgumentNull(
+ () => ODataPathHelper.KeySegmentAsDictionary(keySegment),
+ nameof(keySegment));
+ }
+
+ [Fact]
+ public void GetNextKeySegmentPosition_ReturnsCorrectPosition()
+ {
+ // If the path is Customers(1)/Friends(1001)/Ns.UniqueFriend where Ns.UniqueFriend is a type segment
+ // and 1001 is a KeySegment, and the starting position is index 1, the next keysegment position is index 3.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new NavigationPropertySegment(model.friendsProperty, model.customerSet),
+ new KeySegment(friendsKey, model.personType, null),
+ new TypeSegment(model.uniquePersonType, model.personType, null)
+ });
+
+ // Act
+ int position = ODataPathHelper.GetNextKeySegmentPosition(path.AsList(), 1);
+
+ // Assert
+ Assert.Equal(3, position);
+ }
+
+ [Fact]
+ public void GetNextKeySegmentPosition_ReturnsNegativeOneIfNoKeySegmentIsFound()
+ {
+ // If the path is Customers(1)/Friends(1001)/Ns.UniqueFriend where Ns.UniqueFriend is a type segment
+ // and 1001 is a KeySegment, and the starting position is index 1, the next keysegment position is index 3.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new NavigationPropertySegment(model.friendsProperty, model.customerSet),
+ new TypeSegment(model.uniquePersonType, model.personType, null)
+ });
+
+ // Act
+ int position = ODataPathHelper.GetNextKeySegmentPosition(path.AsList(), 1);
+
+ // Assert
+ Assert.Equal(-1, position);
+ }
+
+ [Fact]
+ public void GetNextKeySegmentPosition_ReturnsNegativeOneIfInvalidPositionIsPassed()
+ {
+ // If the path is Customers(1)/Friends(1001)/Ns.UniqueFriend where Ns.UniqueFriend is a type segment
+ // and 1001 is a KeySegment, and the starting position is index 1, the next keysegment position is index 3.
+
+ // Arrange
+ ODataPath path = new ODataPath(new ODataPathSegment[]
+ {
+ new EntitySetSegment(model.customerSet),
+ new KeySegment(customerKey, model.customerType, model.customerSet),
+ new NavigationPropertySegment(model.friendsProperty, model.customerSet),
+ new TypeSegment(model.uniquePersonType, model.personType, null)
+ });
+
+ // Act
+ int position = ODataPathHelper.GetNextKeySegmentPosition(path.AsList(), 10);
+
+ // Assert
+ Assert.Equal(-1, position);
+ }
+ }
+}
diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/Models/SampleEdmModel.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/Models/SampleEdmModel.cs
new file mode 100644
index 0000000000..89e7db509b
--- /dev/null
+++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/TestCommon/Models/SampleEdmModel.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (c) .NET Foundation and Contributors. All rights reserved.
+// See License.txt in the project root for license information.
+//
+//------------------------------------------------------------------------------
+
+using Microsoft.OData.Edm;
+
+namespace Microsoft.AspNet.OData.Test.Common.Models
+{
+ public class SampleEdmModel
+ {
+ public EdmNavigationProperty friendsProperty;
+ public EdmEntityType customerType;
+ public EdmEntityType personType;
+ public EdmEntityType uniquePersonType;
+ public EdmEntityType vipCustomerType;
+ public IEdmEntitySet customerSet;
+ public EdmModel model;
+
+ public SampleEdmModel()
+ {
+ model = new EdmModel();
+ EdmEntityContainer container = new EdmEntityContainer("NS", "Container");
+
+ personType = new EdmEntityType("NS", "Person");
+ personType.AddKeys(personType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32, isNullable: false));
+ personType.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String, isNullable: false);
+
+ customerType = new EdmEntityType("NS", "Customer");
+ customerType.AddKeys(customerType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32, isNullable: false));
+ customerType.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String, isNullable: false);
+ friendsProperty = customerType.AddUnidirectionalNavigation(
+ new EdmNavigationPropertyInfo
+ {
+ ContainsTarget = true,
+ Name = "Friends",
+ Target = personType,
+ TargetMultiplicity = EdmMultiplicity.Many
+ });
+
+ vipCustomerType = new EdmEntityType("NS", "VipCustomer", customerType);
+ vipCustomerType.AddKeys(vipCustomerType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32, isNullable: false));
+ vipCustomerType.AddStructuralProperty("VipName", EdmPrimitiveTypeKind.String, isNullable: false);
+
+ uniquePersonType = new EdmEntityType("NS", "UniquePerson", personType);
+ uniquePersonType.AddKeys(uniquePersonType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32, isNullable: false));
+ uniquePersonType.AddStructuralProperty("UniqueName", EdmPrimitiveTypeKind.String, isNullable: false);
+
+ model.AddElement(customerType);
+ model.AddElement(personType);
+ model.AddElement(uniquePersonType);
+ model.AddElement(vipCustomerType);
+ model.AddElement(container);
+
+ customerSet = container.AddEntitySet("Customers", customerType);
+ }
+ }
+}
\ No newline at end of file