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/"); }