<|start_filename|>EFCore.SqlServer.HierarchyId.Test/Test/Models/Patriarch.cs<|end_filename|>
﻿namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models
{
    class Patriarch
    {
        public HierarchyId Id { get; set; }
        public string Name { get; set; }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId.Test/Test/Logging/TestLogger.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Logging
{
    public class TestLogger : ILogger
    {
        public string Sql { get; set; }

        public IDisposable BeginScope<TState>(TState state)
            => null;

        public bool IsEnabled(LogLevel logLevel)
            => true;

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (eventId != RelationalEventId.CommandExecuting)
                return;

            var structure = (IReadOnlyList<KeyValuePair<string, object>>)state;
            var commandText = (string)structure.FirstOrDefault(i => i.Key == "commandText").Value;

            Sql = commandText;
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdValueConverter.cs<|end_filename|>
using System.Data.SqlTypes;
using System.IO;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace Microsoft.EntityFrameworkCore.SqlServer.Storage
{
    internal class SqlServerHierarchyIdValueConverter : ValueConverter<HierarchyId, SqlBytes>
    {
        public SqlServerHierarchyIdValueConverter()
            : base(h => toProvider(h), b => fromProvider(b))
        {
        }

        private static SqlBytes toProvider(HierarchyId hid)
        {
            using (var memory = new MemoryStream())
            using (var writer = new BinaryWriter(memory))
            {
                hid.Write(writer);
                return new SqlBytes(memory.ToArray());
            }
        }

        private static HierarchyId fromProvider(SqlBytes bytes)
        {
            using (var memory = new MemoryStream(bytes.Value))
            using (var reader = new BinaryReader(memory))
            {
                return HierarchyId.Read(reader);
            }
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId.Abstractions/HierarchyId.cs<|end_filename|>
﻿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 represengint 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)
            => new HierarchyId(_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)
            => new HierarchyId(_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 greather 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);
    }
}

<|start_filename|>EFCore.InMemory.HierarchyId/Extensions/InMemoryHierarchyIdDbContextOptionsBuilderExtensions.cs<|end_filename|>
using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.InMemory.Infrastructure;

namespace Microsoft.EntityFrameworkCore
{
    /// <summary>
    /// HierarchyId specific extension methods for <see cref="InMemoryDbContextOptionsBuilder"/>.
    /// </summary>
    public static class InMemoryHierarchyIdDbContextOptionsBuilderExtensions
    {
        /// <summary>
        /// Enable HierarchyId mappings.
        /// </summary>
        /// <param name="optionsBuilder">The builder being used to configure in-memory database.</param>
        /// <returns>The options builder so that further configuration can be chained.</returns>
        public static InMemoryDbContextOptionsBuilder UseHierarchyId(
           this InMemoryDbContextOptionsBuilder optionsBuilder)
        {
            // Work around dotnet/efcore#23669
            var optionsBuilderPropertyInfo = optionsBuilder.GetType().GetProperty("OptionsBuilder", BindingFlags.Instance | BindingFlags.NonPublic);
            var coreOptionsBuilder = optionsBuilderPropertyInfo.GetValue(optionsBuilder) as DbContextOptionsBuilder;

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

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

            return optionsBuilder;
        }
    }
}


<|start_filename|>EFCore.InMemory.HierarchyId/Infrastructure/InMemoryHierarchyIdOptionsExtension.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.InMemory.Properties;
using Microsoft.EntityFrameworkCore.InMemory.Storage;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.InMemory.Infrastructure
{
    internal class InMemoryHierarchyIdOptionsExtension : IDbContextOptionsExtension
    {
        private DbContextOptionsExtensionInfo _info;

        public DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);

        public virtual void ApplyServices(IServiceCollection services)
        {
            services.AddEntityFrameworkInMemoryHierarchyId();
        }

        public virtual void Validate(IDbContextOptions options)
        {
            var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
            if (internalServiceProvider != null)
            {
                using (var scope = internalServiceProvider.CreateScope())
                {
                    if (scope.ServiceProvider.GetService<IEnumerable<ITypeMappingSourcePlugin>>()
                           ?.Any(s => s is InMemoryHierarchyIdTypeMappingSourcePlugin) != true)
                    {
                        throw new InvalidOperationException(Resources.ServicesMissing);
                    }
                }
            }
        }

        private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
        {
            public ExtensionInfo(IDbContextOptionsExtension extension)
                : base(extension)
            {
            }

            private new InMemoryHierarchyIdOptionsExtension Extension
                => (InMemoryHierarchyIdOptionsExtension)base.Extension;

            public override bool IsDatabaseProvider => false;

            public override long GetServiceProviderHashCode() => 0;

            public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
            => debugInfo["InMemory:" + nameof(InMemoryHierarchyIdDbContextOptionsBuilderExtensions.UseHierarchyId)] = "1";

            public override string LogFragment => "using HierarchyId ";
        }
    }
}


<|start_filename|>EFCore.InMemory.HierarchyId/Extensions/InMemoryHierarchyIdServiceCollectionExtensions.cs<|end_filename|>
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.InMemory.Storage;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// EntityFrameworkCore.InMemory.HierarchyId extension methods for <see cref="IServiceCollection"/>.
    /// </summary>
    public static class InMemoryHierarchyIdServiceCollectionExtensions
    {
        /// <summary>
        /// Adds the services required for HierarchyId support in the in-memory database provider for Entity Framework.
        /// </summary>
        /// <param name="serviceCollection">The <see cref="IServiceCollection"/> to add services to.</param>
        /// <returns>The same service collection so that multiple calls can be chained.</returns>
        public static IServiceCollection AddEntityFrameworkInMemoryHierarchyId(
            this IServiceCollection serviceCollection)
        {
            new EntityFrameworkServicesBuilder(serviceCollection)
                .TryAddProviderSpecificServices(
                    x => x
                        .TryAddSingletonEnumerable<ITypeMappingSourcePlugin, InMemoryHierarchyIdTypeMappingSourcePlugin>()
                );

            return serviceCollection;
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdDbContextOptionsBuilderExtensions.cs<|end_filename|>
﻿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;
        }
    }
}


<|start_filename|>EFCore.InMemory.HierarchyId/Storage/InMemoryHierarchyIdTypeMapping.cs<|end_filename|>
using System;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace Microsoft.EntityFrameworkCore.InMemory.Storage
{
    internal class InMemoryHierarchyIdTypeMapping : CoreTypeMapping
    {
        public InMemoryHierarchyIdTypeMapping(Type clrType)
            : base(new CoreTypeMappingParameters(clrType))
        {
        }

        private InMemoryHierarchyIdTypeMapping(CoreTypeMappingParameters parameters)
            : base(parameters)
        {
        }

        public override CoreTypeMapping Clone(ValueConverter converter)
            => new InMemoryHierarchyIdTypeMapping(Parameters.WithComposedConverter(converter));
    }
}


<|start_filename|>EFCore.InMemory.HierarchyId/Storage/InMemoryHierarchyIdTypeMappingSourcePlugin.cs<|end_filename|>
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.InMemory.Storage
{
    internal class InMemoryHierarchyIdTypeMappingSourcePlugin : ITypeMappingSourcePlugin
    {
        public CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo)
        {
            var clrType = mappingInfo.ClrType;

            return typeof(HierarchyId).IsAssignableFrom(clrType)
                ? new InMemoryHierarchyIdTypeMapping(clrType)
                : null;
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId.Test/DesignTimeServicesTests.cs<|end_filename|>
﻿using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.SqlServer.Design;
using Microsoft.EntityFrameworkCore.SqlServer.Scaffolding;
using Microsoft.EntityFrameworkCore.SqlServer.Storage;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Microsoft.EntityFrameworkCore.SqlServer
{
    public class DesignTimeServicesTests
    {
        [Fact]
        public void ConfigureDesignTimeServices_works()
        {
            var serviceCollection = new ServiceCollection();
            new SqlServerHierarchyIdDesignTimeServices().ConfigureDesignTimeServices(serviceCollection);
            var serviceProvider = serviceCollection.BuildServiceProvider();

            Assert.IsType<SqlServerHierarchyIdTypeMappingSourcePlugin>(serviceProvider.GetService<IRelationalTypeMappingSourcePlugin>());
            Assert.IsType<SqlServerHierarchyIdCodeGeneratorPlugin>(serviceProvider.GetService<IProviderCodeGeneratorPlugin>());
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs<|end_filename|>
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.SqlServer.Query.ExpressionTranslators
{
    internal class SqlServerHierarchyIdMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
    {
        public SqlServerHierarchyIdMethodCallTranslatorPlugin(
            IRelationalTypeMappingSource typeMappingSource,
            ISqlExpressionFactory sqlExpressionFactory)
        {
            Translators = new IMethodCallTranslator[]
            {
                new SqlServerHierarchyIdMethodTranslator(typeMappingSource, sqlExpressionFactory)
            };
        }

        public virtual IEnumerable<IMethodCallTranslator> Translators { get; }
    }
}


<|start_filename|>EFCore.InMemory.HierarchyId.Test/DatabaseContextTests.cs<|end_filename|>
﻿using System.Linq;
using Microsoft.EntityFrameworkCore.SqlServer.Test.Models;
using Xunit;

namespace Microsoft.EntityFrameworkCore.SqlServer
{
    public class DatabaseContextTests
    {
        [Fact]
        public void Should_CreateDatabaseContext()
        {
            // Arrange
            var databaseContext = new AbrahamicContext();

            // Act
            databaseContext.Database.EnsureCreated();

            // Assert
            Assert.NotNull(databaseContext);
            Assert.NotNull(databaseContext.Patriarchy);
            Assert.Equal(16, databaseContext.Patriarchy.Count());
        }

        [Fact]
        public void Should_GetEphraimEntryByName()
        {
            // Arrange
            var databaseContext = new AbrahamicContext();

            // Act
            databaseContext.Database.EnsureCreated();
            var ephraim = databaseContext.Patriarchy.Single(w => w.Name == "Ephraim");

            // Assert
            Assert.Equal(HierarchyId.Parse("/1/1/11.1/"), ephraim.Id);
        }

        [Fact]
        public void Should_GetEphraimEntryById()
        {
            // Arrange
            var databaseContext = new AbrahamicContext();

            // Act
            databaseContext.Database.EnsureCreated();
            var ephraim = databaseContext.Patriarchy.Single(w => w.Id == HierarchyId.Parse("/1/1/11.1/"));

            // Assert
            Assert.Equal("Ephraim", ephraim.Name);
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId.Test/NullabilityTests.cs<|end_filename|>
﻿using System.Linq;
using Xunit;

namespace Microsoft.EntityFrameworkCore.SqlServer
{
    public class NullabilityTests
    {
        [Fact]
        public void Null_against_null()
        {
            Assert.True((HierarchyId)null == (HierarchyId)null);
            Assert.False((HierarchyId)null != (HierarchyId)null);
            Assert.False((HierarchyId)null > (HierarchyId)null);
            Assert.False((HierarchyId)null >= (HierarchyId)null);
            Assert.False((HierarchyId)null < (HierarchyId)null);
            Assert.False((HierarchyId)null <= (HierarchyId)null);
        }

        [Fact]
        public void Null_against_nonNull()
        {
            var hid = HierarchyId.GetRoot();
            Assert.False(hid == (HierarchyId)null);
            Assert.False((HierarchyId)null == hid);

            Assert.True(hid != (HierarchyId)null);
            Assert.True((HierarchyId)null != hid);

            Assert.False(hid > (HierarchyId)null);
            Assert.False((HierarchyId)null > hid);

            Assert.False(hid >= (HierarchyId)null);
            Assert.False((HierarchyId)null >= hid);

            Assert.False(hid < (HierarchyId)null);
            Assert.False((HierarchyId)null < hid);

            Assert.False(hid <= (HierarchyId)null);
            Assert.False((HierarchyId)null <= hid);
        }

        [Fact]
        public void NullOnly_aggregates_equalTo_null()
        {
            var hid = (HierarchyId)null;
            var collection = new[] { (HierarchyId)null, (HierarchyId)null, };
            var min = collection.Min();
            var max = collection.Max();

            Assert.True(hid == min);
            Assert.True(min == hid);
            Assert.False(hid != min);
            Assert.False(min != hid);

            Assert.True(hid == max);
            Assert.True(max == hid);
            Assert.False(hid != max);
            Assert.False(max != hid);
        }

        [Fact]
        public void Aggregates_including_nulls_equalTo_nonNull()
        {
            var hid = HierarchyId.GetRoot();
            var collection = new[] { (HierarchyId)null, (HierarchyId)null, HierarchyId.GetRoot(), HierarchyId.GetRoot(), };
            var min = collection.Min();
            var max = collection.Max();

            Assert.True(hid == min);
            Assert.True(min == hid);
            Assert.False(hid != min);
            Assert.False(min != hid);

            Assert.True(hid == max);
            Assert.True(max == hid);
            Assert.False(hid != max);
            Assert.False(max != hid);
        }
    }
}


<|start_filename|>EFCore.SqlServer.HierarchyId/Properties/InternalsVisibleTo.cs<|end_filename|>
﻿using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo(
    "EntityFrameworkCore.SqlServer.HierarchyId.Test, PublicKey=<KEY>")]


<|start_filename|>EFCore.SqlServer.HierarchyId/Design/SqlServerHierarchyIdDesignTimeServices.cs<|end_filename|>
﻿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>();
        }
    }
}
