Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
bricelam committed Feb 14, 2023
2 parents 09b4820 + ee8db40 commit 7caf9c4
Show file tree
Hide file tree
Showing 41 changed files with 2,838 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ updates:
- dependency-name: Microsoft.CSharp
- dependency-name: Microsoft.Data.SqlClient
- dependency-name: Microsoft.DotNet.PlatformAbstractions
- dependency-name: Microsoft.SqlServer.Types
- dependency-name: mod_spatialite
- dependency-name: Mono.TextTemplating
- dependency-name: NetTopologySuite*
Expand Down
21 changes: 21 additions & 0 deletions All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Trimming.Tests", "te
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Templates", "src\EFCore.Templates\EFCore.Templates.csproj", "{1FE385D8-8F8B-4EC9-A1A9-AFCC38B8546C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SqlServer.HierarchyId", "src\EFCore.SqlServer.HierarchyId\EFCore.SqlServer.HierarchyId.csproj", "{8F722A02-71A4-4787-ACD8-FB7D5B7AE648}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SqlServer.HierarchyId.Tests", "test\EFCore.SqlServer.HierarchyId.Tests\EFCore.SqlServer.HierarchyId.Tests.csproj", "{01F86E65-6448-424C-AAB5-9C6427EF6FD4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SqlServer.Abstractions", "src\EFCore.SqlServer.Abstractions\EFCore.SqlServer.Abstractions.csproj", "{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -331,6 +337,18 @@ Global
{1FE385D8-8F8B-4EC9-A1A9-AFCC38B8546C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FE385D8-8F8B-4EC9-A1A9-AFCC38B8546C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FE385D8-8F8B-4EC9-A1A9-AFCC38B8546C}.Release|Any CPU.Build.0 = Release|Any CPU
{8F722A02-71A4-4787-ACD8-FB7D5B7AE648}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F722A02-71A4-4787-ACD8-FB7D5B7AE648}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F722A02-71A4-4787-ACD8-FB7D5B7AE648}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F722A02-71A4-4787-ACD8-FB7D5B7AE648}.Release|Any CPU.Build.0 = Release|Any CPU
{01F86E65-6448-424C-AAB5-9C6427EF6FD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01F86E65-6448-424C-AAB5-9C6427EF6FD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01F86E65-6448-424C-AAB5-9C6427EF6FD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01F86E65-6448-424C-AAB5-9C6427EF6FD4}.Release|Any CPU.Build.0 = Release|Any CPU
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -386,6 +404,9 @@ Global
{F1B2E5A0-8C74-414A-B262-353FEE325E9F} = {258D5057-81B9-40EC-A872-D21E27452749}
{933C8662-817C-4F45-B98B-6557E28F7BB1} = {258D5057-81B9-40EC-A872-D21E27452749}
{1FE385D8-8F8B-4EC9-A1A9-AFCC38B8546C} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{8F722A02-71A4-4787-ACD8-FB7D5B7AE648} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{01F86E65-6448-424C-AAB5-9C6427EF6FD4} = {258D5057-81B9-40EC-A872-D21E27452749}
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {285A5EB4-BCF4-40EB-B9E1-DF6DBCB5E705}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>EntityFrameworkCore.SqlServer.HierarchyId.Abstractions</AssemblyName>
<RootNamespace>Microsoft.EntityFrameworkCore</RootNamespace>
<Description>Common abstractions for using hierarchyid with EF Core</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="dotMorten.Microsoft.SqlServer.Types" Version="1.5.0" />
</ItemGroup>

</Project>
217 changes: 217 additions & 0 deletions src/EFCore.SqlServer.Abstractions/HierarchyId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
using System;
using System.IO;
using Microsoft.SqlServer.Types;

namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// Represents a position in a hierarchical structure, specifying depth and breadth.
/// </summary>
public class HierarchyId : IComparable
{
private SqlHierarchyId _value;

private HierarchyId(SqlHierarchyId value)
{
if (value.IsNull)
throw new ArgumentNullException(nameof(value));

_value = value;
}

/// <summary>
/// Gets the root node of the hierarchy.
/// </summary>
/// <returns>The root node of the hierarchy.</returns>
public static HierarchyId GetRoot()
=> new HierarchyId(SqlHierarchyId.GetRoot());

/// <summary>
/// Converts the canonical string representation of a node to a <see cref="HierarchyId"/> value.
/// </summary>
/// <param name="input">The string representation of a node.</param>
/// <returns>A <see cref="HierarchyId"/> value.</returns>
public static HierarchyId Parse(string input)
=> Wrap(SqlHierarchyId.Parse(input));

/// <summary>
/// Reads a <see cref="HierarchyId"/> value from the specified reader.
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>A <see cref="HierarchyId"/> value.</returns>
public static HierarchyId Read(BinaryReader reader)
{
var hid = new SqlHierarchyId();
hid.Read(reader);
return Wrap(hid);
}

/// <summary>
/// Writes this <see cref="HierarchyId"/> value to the specified writer.
/// </summary>
/// <param name="writer">The writer.</param>
public void Write(BinaryWriter writer)
{
_value.Write(writer);
}

/// <inheritdoc/>
public int CompareTo(object obj)
=> _value.CompareTo(
obj is HierarchyId other
? other._value
: obj);

/// <inheritdoc/>
public override bool Equals(object obj)
=> _value.Equals(
obj is HierarchyId other
? other._value
: obj);

/// <summary>
/// Gets the node <paramref name="n"/> levels up the hierarchical tree.
/// </summary>
/// <param name="n">The number of levels to ascend in the hierarchy.</param>
/// <returns>A <see cref="HierarchyId"/> value representing the <paramref name="n"/>th ancestor of this node or null if <paramref name="n"/> is greater than <see cref="GetLevel"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="n"/> is negative.</exception>
public HierarchyId GetAncestor(int n)
=> Wrap(_value.GetAncestor(n));

/// <summary>
/// Gets the value of a descendant node that is greater than <paramref name="child1"/> and less than <paramref name="child2"/>.
/// </summary>
/// <param name="child1">The lower bound.</param>
/// <param name="child2">The upper bound.</param>
/// <returns>A <see cref="HierarchyId"/> value.</returns>
public HierarchyId GetDescendant(HierarchyId child1, HierarchyId child2)
=> Wrap(_value.GetDescendant(Unwrap(child1), Unwrap(child2)));

/// <inheritdoc/>
public override int GetHashCode()
=> _value.GetHashCode();

/// <summary>
/// Gets the level of this node in the hierarchical tree.
/// </summary>
/// <returns>The depth of this node. The root node is level 0.</returns>
public short GetLevel()
=> _value.GetLevel().Value;

/// <summary>
/// Gets a value representing the location of a new node that has a path from <paramref name="newRoot"/> equal to the path from <paramref name="oldRoot"/> to this, effectively moving this to the new location.
/// </summary>
/// <param name="oldRoot">An ancestor of this node specifying the endpoint of the path segment to be moved.</param>
/// <param name="newRoot">The node that represents the new ancestor.</param>
/// <returns>A <see cref="HierarchyId"/> value or null if <paramref name="oldRoot"/> or <paramref name="newRoot"/> is null.</returns>
public HierarchyId GetReparentedValue(HierarchyId oldRoot, HierarchyId newRoot)
=> Wrap(_value.GetReparentedValue(Unwrap(oldRoot), Unwrap(newRoot)));

/// <summary>
/// Gets a value indicating whether this node is a descendant of <paramref name="parent"/>.
/// </summary>
/// <param name="parent">The parent to test against.</param>
/// <returns>True if this node is in the sub-tree rooted at <paramref name="parent"/>; otherwise false.</returns>
public bool IsDescendantOf(HierarchyId parent)
{
if (parent == null)
return false;

return _value.IsDescendantOf(parent._value).Value;
}

/// <inheritdoc/>
public override string ToString()
=> _value.ToString();

/// <summary>
/// Evaluates whether two nodes are equal.
/// </summary>
/// <param name="hid1">The first node to compare.</param>
/// <param name="hid2">The second node to compare.</param>
/// <returns>True if <paramref name="hid1"/> and <paramref name="hid2"/> are equal; otherwise, false.</returns>
public static bool operator ==(HierarchyId hid1, HierarchyId hid2)
{
var sh1 = Unwrap(hid1);
var sh2 = Unwrap(hid2);

return sh1.IsNull == sh2.IsNull && sh1.CompareTo(sh2) == 0;
}

/// <summary>
/// Evaluates whether two nodes are unequal.
/// </summary>
/// <param name="hid1">The first node to compare.</param>
/// <param name="hid2">The second node to compare.</param>
/// <returns>True if <paramref name="hid1"/> and <paramref name="hid2"/> are unequal; otherwise, false.</returns>
public static bool operator !=(HierarchyId hid1, HierarchyId hid2)
{
var sh1 = Unwrap(hid1);
var sh2 = Unwrap(hid2);

return sh1.IsNull != sh2.IsNull || sh1.CompareTo(sh2) != 0;
}

/// <summary>
/// Evaluates whether one node is less than another.
/// </summary>
/// <param name="hid1">The first node to compare.</param>
/// <param name="hid2">The second node to compare.</param>
/// <returns>True if <paramref name="hid1"/> is less than <paramref name="hid2"/>; otherwise, false.</returns>
public static bool operator <(HierarchyId hid1, HierarchyId hid2)
{
var sh1 = Unwrap(hid1);
var sh2 = Unwrap(hid2);

return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) < 0;
}

/// <summary>
/// Evaluates whether one node is greater than another.
/// </summary>
/// <param name="hid1">The first node to compare.</param>
/// <param name="hid2">The second node to compare.</param>
/// <returns>True if <paramref name="hid1"/> is greater than <paramref name="hid2"/>; otherwise, false.</returns>
public static bool operator >(HierarchyId hid1, HierarchyId hid2)
{
var sh1 = Unwrap(hid1);
var sh2 = Unwrap(hid2);

return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) > 0;
}

/// <summary>
/// Evaluates whether one node is less than or equal to another.
/// </summary>
/// <param name="hid1">The first node to compare.</param>
/// <param name="hid2">The second node to compare.</param>
/// <returns>True if <paramref name="hid1"/> is less than or equal to <paramref name="hid2"/>; otherwise, false.</returns>
public static bool operator <=(HierarchyId hid1, HierarchyId hid2)
{
var sh1 = Unwrap(hid1);
var sh2 = Unwrap(hid2);

return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) <= 0;
}

/// <summary>
/// Evaluates whether one node is greater than or equal to another.
/// </summary>
/// <param name="hid1">The first node to compare.</param>
/// <param name="hid2">The second node to compare.</param>
/// <returns>True if <paramref name="hid1"/> is greater than or equal to <paramref name="hid2"/>; otherwise, false.</returns>
public static bool operator >=(HierarchyId hid1, HierarchyId hid2)
{
var sh1 = Unwrap(hid1);
var sh2 = Unwrap(hid2);

return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) >= 0;
}

private static SqlHierarchyId Unwrap(HierarchyId value)
=> value?._value ?? SqlHierarchyId.Null;

private static HierarchyId Wrap(SqlHierarchyId value)
=> value.IsNull ? null : new HierarchyId(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.SqlServer.Scaffolding;
using Microsoft.EntityFrameworkCore.SqlServer.Storage;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.SqlServer.Design
{
/// <summary>
/// Enables configuring design-time services. Tools will automatically discover implementations of this
/// interface that are in the startup assembly.
/// </summary>
public class SqlServerHierarchyIdDesignTimeServices : IDesignTimeServices
{
/// <summary>
/// Configures design-time services. Use this method to override the default design-time services with your
/// own implementations.
/// </summary>
/// <param name="serviceCollection"> The design-time service collection. </param>
public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
{
serviceCollection
.AddSingleton<IRelationalTypeMappingSourcePlugin, SqlServerHierarchyIdTypeMappingSourcePlugin>()
.AddSingleton<IProviderCodeGeneratorPlugin, SqlServerHierarchyIdCodeGeneratorPlugin>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>EntityFrameworkCore.SqlServer.HierarchyId</AssemblyName>
<RootNamespace>Microsoft.EntityFrameworkCore.SqlServer</RootNamespace>
<Description>Adds hierarchyid support to the SQL Server EF Core provider</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<None Include="build\**\*">
<Pack>True</Pack>
<PackagePath>build</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EFCore.SqlServer.HierarchyId.Abstractions\EFCore.SqlServer.HierarchyId.Abstractions.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure;

namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// HierarchyId specific extension methods for <see cref="SqlServerDbContextOptionsBuilder"/>.
/// </summary>
public static class SqlServerHierarchyIdDbContextOptionsBuilderExtensions
{
/// <summary>
/// Enable HierarchyId mappings.
/// </summary>
/// <param name="optionsBuilder">The builder being used to configure SQL Server.</param>
/// <returns>The options builder so that further configuration can be chained.</returns>
public static SqlServerDbContextOptionsBuilder UseHierarchyId(
this SqlServerDbContextOptionsBuilder optionsBuilder)
{
var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;

var extension = coreOptionsBuilder.Options.FindExtension<SqlServerHierarchyIdOptionsExtension>()
?? new SqlServerHierarchyIdOptionsExtension();

((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}
}
}
Loading

0 comments on commit 7caf9c4

Please sign in to comment.