diff --git a/src/EFCore.SqlServer.Abstractions/HierarchyId.cs b/src/EFCore.SqlServer.Abstractions/HierarchyId.cs
index e1bffb4d185..04dc58d058d 100644
--- a/src/EFCore.SqlServer.Abstractions/HierarchyId.cs
+++ b/src/EFCore.SqlServer.Abstractions/HierarchyId.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using System.Text;
using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.SqlServer.Types;
@@ -25,7 +26,7 @@ public HierarchyId()
}
///
- /// Initializes a new instance of the class. Equivalent to .
+ /// Initializes a new instance of the class. Equivalent to .
///
/// The string representation of the node.
public HierarchyId(string value)
@@ -63,6 +64,35 @@ public static HierarchyId GetRoot()
public static HierarchyId? Parse(string? input)
=> (HierarchyId?)SqlHierarchyId.Parse(input);
+ ///
+ /// Converts the and of a node to a value.
+ ///
+ /// The parent HierarchyId of node.
+ /// The parent Id of current node. It can be more than one element if want have path like: "/1/2/3.1/", otherwise one element for have path like: "/1/2/3/".
+ /// A value.
+ public static HierarchyId Parse(HierarchyId parentHierarchyId , params int[] parentId)
+ => GenerateHierarchyIdBasedOnParent(parentHierarchyId, parentId);
+
+ //This Method can move to "SqlHierarchyId in Microsoft.SqlServer.Types", if we don't want put it in this abstraction.
+ private static HierarchyId GenerateHierarchyIdBasedOnParent(HierarchyId parent, params int[] parentId)
+ {
+ if (parent is null)
+ {
+ return HierarchyId.GetRoot();
+ }
+
+ if (parentId.Length < 1)
+ {
+ return parent;
+ }
+
+ var specificPath = new StringBuilder(parent.ToString());
+ specificPath.Append(string.Join(".", parentId));
+ specificPath.Append('/');
+
+ return HierarchyId.Parse(specificPath.ToString());
+ }
+
///
public virtual int CompareTo(HierarchyId? other)
=> _value.CompareTo((SqlHierarchyId)other);
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs
index 475aacbbdab..02672cd176d 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs
@@ -35,4 +35,30 @@ public void GetReparentedValue_returns_null_when_newRoot_is_null()
[ConditionalFact]
public void IsDescendantOf_returns_false_when_parent_is_null()
=> Assert.False(HierarchyId.Parse("/1/").IsDescendantOf(null));
+
+ [ConditionalFact]
+ public void Parse_overloads_works_when_parentId_is_simpleId()
+ => Assert.Equal(HierarchyId.Parse(_parent, 2), HierarchyId.Parse("/1/2/"));
+
+ [ConditionalFact]
+ public void Parse_overloads_works_when_parentId_is_dottedString()
+ => Assert.Equal(HierarchyId.Parse(_parent, 2,1), HierarchyId.Parse("/1/2.1/"));
+
+ [ConditionalFact]
+ public void Parse_overloads_works_when_parentId_is_empty()
+ => Assert.Equal(HierarchyId.Parse(_parent), HierarchyId.Parse("/1/"));
+
+ [ConditionalFact]
+ public void Parse_overloads_works_when_parentHierarchy_is_root_and_parentId_is_simple()
+ => Assert.Equal(HierarchyId.Parse(HierarchyId.GetRoot(),1), HierarchyId.Parse("/1/"));
+
+ [ConditionalFact]
+ public void Parse_overloads_works_when_parentHierarchy_is_root_and_parentId_is_empty()
+ => Assert.Equal(HierarchyId.Parse(HierarchyId.GetRoot()), HierarchyId.Parse("/"));
+
+ [ConditionalFact]
+ public void Parse_overloads_works_when_parentHierarchy_is_null_and_parentId_is_empty()
+ => Assert.Equal(HierarchyId.Parse(null,[]), HierarchyId.Parse("/"));
+
+ private readonly HierarchyId _parent = HierarchyId.Parse("/1/");
}