diff --git a/eng/helix.proj b/eng/helix.proj
index 0d1b61f3902..2a9cc330859 100644
--- a/eng/helix.proj
+++ b/eng/helix.proj
@@ -12,7 +12,7 @@
true
true
- $(RepoRoot)/test/EFCore.SqlServer.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.OData.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.AspNet.SqlServer.FunctionalTests/*.csproj
+ $(RepoRoot)/test/EFCore.SqlServer.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.SqlServer.HierarchyId.Tests/*.csproj;$(RepoRoot)/test/EFCore.OData.FunctionalTests/*.csproj;$(RepoRoot)/test/EFCore.AspNet.SqlServer.FunctionalTests/*.csproj
diff --git a/src/EFCore.Abstractions/ChangeTracking/Internal/SortableBindingList.cs b/src/EFCore.Abstractions/ChangeTracking/Internal/SortableBindingList.cs
index d51d999793f..d6eff18c86f 100644
--- a/src/EFCore.Abstractions/ChangeTracking/Internal/SortableBindingList.cs
+++ b/src/EFCore.Abstractions/ChangeTracking/Internal/SortableBindingList.cs
@@ -43,8 +43,8 @@ public SortableBindingList(List list)
"ReflectionAnalysis",
"IL2046",
Justification =
- "This method is an override, and the base method isn't annotated with RequiresUnreferencedCode. " +
- "The entire type is marked with RequiresUnreferencedCode.")]
+ "This method is an override, and the base method isn't annotated with RequiresUnreferencedCode. "
+ + "The entire type is marked with RequiresUnreferencedCode.")]
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
if (PropertyComparer.CanSort(prop.PropertyType))
diff --git a/src/EFCore.Abstractions/ChangeTracking/ObservableCollectionListSource.cs b/src/EFCore.Abstractions/ChangeTracking/ObservableCollectionListSource.cs
index 07458d41a35..0aa5deb7fb4 100644
--- a/src/EFCore.Abstractions/ChangeTracking/ObservableCollectionListSource.cs
+++ b/src/EFCore.Abstractions/ChangeTracking/ObservableCollectionListSource.cs
@@ -80,8 +80,8 @@ bool IListSource.ContainsListCollection
"ReflectionAnalysis",
"IL2046",
Justification =
- "This method is an interface implementation, and the interface method isn't annotated with RequiresUnreferencedCode. " +
- "The entire type is marked with RequiresUnreferencedCode.")]
+ "This method is an interface implementation, and the interface method isn't annotated with RequiresUnreferencedCode. "
+ + "The entire type is marked with RequiresUnreferencedCode.")]
IList IListSource.GetList()
=> _bindingList ??= this.ToBindingList();
}
diff --git a/src/EFCore.SqlServer.Abstractions/EFCore.SqlServer.Abstractions.csproj b/src/EFCore.SqlServer.Abstractions/EFCore.SqlServer.Abstractions.csproj
index ae4835d4b17..a62ea8f849a 100644
--- a/src/EFCore.SqlServer.Abstractions/EFCore.SqlServer.Abstractions.csproj
+++ b/src/EFCore.SqlServer.Abstractions/EFCore.SqlServer.Abstractions.csproj
@@ -1,16 +1,22 @@
- netstandard2.0
- EntityFrameworkCore.SqlServer.HierarchyId.Abstractions
+ netstandard2.1
+ 3.6
+ Microsoft.EntityFrameworkCore.SqlServer.Abstractions
Microsoft.EntityFrameworkCore
- Common abstractions for using hierarchyid with EF Core
- true
+ Provides abstractions that are used by models in conjunction with the SQL Server EF Core provider
+
+ Commonly Used Types:
+ Microsoft.EntityFrameworkCore.HierarchyId
+
true
+ $(PackageTags);SQL Server;HierarchyId
+ true
-
+
diff --git a/src/EFCore.SqlServer.Abstractions/HierarchyId.cs b/src/EFCore.SqlServer.Abstractions/HierarchyId.cs
index 67b4084361c..ec4f4867bb0 100644
--- a/src/EFCore.SqlServer.Abstractions/HierarchyId.cs
+++ b/src/EFCore.SqlServer.Abstractions/HierarchyId.cs
@@ -1,217 +1,216 @@
-using System;
-using System.IO;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
using Microsoft.SqlServer.Types;
-namespace Microsoft.EntityFrameworkCore
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Represents a position in a hierarchical structure, specifying depth and breadth.
+///
+public class HierarchyId : IComparable
{
+ private SqlHierarchyId _value;
+
+ private HierarchyId(SqlHierarchyId value)
+ {
+ if (value.IsNull)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ _value = value;
+ }
+
+ ///
+ /// Gets the root node of the hierarchy.
+ ///
+ /// The root node of the hierarchy.
+ public static HierarchyId GetRoot()
+ => new HierarchyId(SqlHierarchyId.GetRoot());
+
+ ///
+ /// Converts the canonical string representation of a node to a value.
+ ///
+ /// The string representation of a node.
+ /// A value.
+ [return: NotNullIfNotNull(nameof(input))]
+ public static HierarchyId? Parse(string? input)
+ => Wrap(SqlHierarchyId.Parse(input));
+
///
- /// Represents a position in a hierarchical structure, specifying depth and breadth.
+ /// Reads a value from the specified reader.
///
- public class HierarchyId : IComparable
+ /// The reader.
+ /// A value.
+ public static HierarchyId? Read(BinaryReader reader)
{
- private SqlHierarchyId _value;
+ var hid = new SqlHierarchyId();
+ hid.Read(reader);
+ return Wrap(hid);
+ }
- private HierarchyId(SqlHierarchyId value)
- {
- if (value.IsNull)
- throw new ArgumentNullException(nameof(value));
+ ///
+ /// Writes this value to the specified writer.
+ ///
+ /// The writer.
+ public void Write(BinaryWriter writer)
+ {
+ _value.Write(writer);
+ }
- _value = value;
- }
+ ///
+ public int CompareTo(object? obj)
+ => _value.CompareTo(
+ obj is HierarchyId or null
+ ? Unwrap((HierarchyId?)obj)
+ : obj);
- ///
- /// Gets the root node of the hierarchy.
- ///
- /// The root node of the hierarchy.
- public static HierarchyId GetRoot()
- => new HierarchyId(SqlHierarchyId.GetRoot());
-
- ///
- /// Converts the canonical string representation of a node to a value.
- ///
- /// The string representation of a node.
- /// A value.
- public static HierarchyId Parse(string input)
- => Wrap(SqlHierarchyId.Parse(input));
-
- ///
- /// Reads a value from the specified reader.
- ///
- /// The reader.
- /// A value.
- public static HierarchyId Read(BinaryReader reader)
- {
- var hid = new SqlHierarchyId();
- hid.Read(reader);
- return Wrap(hid);
- }
+ ///
+ public override bool Equals(object? obj)
+ => _value.Equals(
+ obj is HierarchyId other
+ ? other._value
+ : obj);
- ///
- /// Writes this value to the specified writer.
- ///
- /// The writer.
- public void Write(BinaryWriter writer)
- {
- _value.Write(writer);
- }
+ ///
+ /// Gets the node levels up the hierarchical tree.
+ ///
+ /// The number of levels to ascend in the hierarchy.
+ /// A value representing the th ancestor of this node or null if is greater than .
+ /// is negative.
+ public HierarchyId GetAncestor(int n)
+ => Wrap(_value.GetAncestor(n))!;
- ///
- public int CompareTo(object obj)
- => _value.CompareTo(
- obj is HierarchyId other
- ? other._value
- : obj);
-
- ///
- public override bool Equals(object obj)
- => _value.Equals(
- obj is HierarchyId other
- ? other._value
- : obj);
-
- ///
- /// Gets the node levels up the hierarchical tree.
- ///
- /// The number of levels to ascend in the hierarchy.
- /// A value representing the th ancestor of this node or null if is greater than .
- /// is negative.
- public HierarchyId GetAncestor(int n)
- => Wrap(_value.GetAncestor(n));
-
- ///
- /// Gets the value of a descendant node that is greater than and less than .
- ///
- /// The lower bound.
- /// The upper bound.
- /// A value.
- public HierarchyId GetDescendant(HierarchyId child1, HierarchyId child2)
- => Wrap(_value.GetDescendant(Unwrap(child1), Unwrap(child2)));
-
- ///
- public override int GetHashCode()
- => _value.GetHashCode();
-
- ///
- /// Gets the level of this node in the hierarchical tree.
- ///
- /// The depth of this node. The root node is level 0.
- public short GetLevel()
- => _value.GetLevel().Value;
-
- ///
- /// Gets a value representing the location of a new node that has a path from equal to the path from to this, effectively moving this to the new location.
- ///
- /// An ancestor of this node specifying the endpoint of the path segment to be moved.
- /// The node that represents the new ancestor.
- /// A value or null if or is null.
- public HierarchyId GetReparentedValue(HierarchyId oldRoot, HierarchyId newRoot)
- => Wrap(_value.GetReparentedValue(Unwrap(oldRoot), Unwrap(newRoot)));
-
- ///
- /// Gets a value indicating whether this node is a descendant of .
- ///
- /// The parent to test against.
- /// True if this node is in the sub-tree rooted at ; otherwise false.
- public bool IsDescendantOf(HierarchyId parent)
- {
- if (parent == null)
- return false;
+ ///
+ /// Gets the value of a descendant node that is greater than and less than .
+ ///
+ /// The lower bound.
+ /// The upper bound.
+ /// A value.
+ public HierarchyId GetDescendant(HierarchyId? child1, HierarchyId? child2)
+ => Wrap(_value.GetDescendant(Unwrap(child1), Unwrap(child2)))!;
- return _value.IsDescendantOf(parent._value).Value;
- }
+ ///
+ public override int GetHashCode()
+ => _value.GetHashCode();
- ///
- public override string ToString()
- => _value.ToString();
-
- ///
- /// Evaluates whether two nodes are equal.
- ///
- /// The first node to compare.
- /// The second node to compare.
- /// True if and are equal; otherwise, false.
- public static bool operator ==(HierarchyId hid1, HierarchyId hid2)
- {
- var sh1 = Unwrap(hid1);
- var sh2 = Unwrap(hid2);
+ ///
+ /// Gets the level of this node in the hierarchical tree.
+ ///
+ /// The depth of this node. The root node is level 0.
+ public short GetLevel()
+ => _value.GetLevel().Value;
- return sh1.IsNull == sh2.IsNull && sh1.CompareTo(sh2) == 0;
- }
+ ///
+ /// Gets a value representing the location of a new node that has a path from equal to the path from to this, effectively moving this to the new location.
+ ///
+ /// An ancestor of this node specifying the endpoint of the path segment to be moved.
+ /// The node that represents the new ancestor.
+ /// A value or null if or is null.
+ public HierarchyId? GetReparentedValue(HierarchyId? oldRoot, HierarchyId? newRoot)
+ => Wrap(_value.GetReparentedValue(Unwrap(oldRoot), Unwrap(newRoot)));
- ///
- /// Evaluates whether two nodes are unequal.
- ///
- /// The first node to compare.
- /// The second node to compare.
- /// True if and are unequal; otherwise, false.
- public static bool operator !=(HierarchyId hid1, HierarchyId hid2)
- {
- var sh1 = Unwrap(hid1);
- var sh2 = Unwrap(hid2);
+ ///
+ /// Gets a value indicating whether this node is a descendant of .
+ ///
+ /// The parent to test against.
+ /// True if this node is in the sub-tree rooted at ; otherwise false.
+ public bool IsDescendantOf(HierarchyId? parent)
+ => _value.IsDescendantOf(Unwrap(parent)).IsTrue;
- return sh1.IsNull != sh2.IsNull || sh1.CompareTo(sh2) != 0;
- }
+ ///
+ public override string ToString()
+ => _value.ToString();
- ///
- /// Evaluates whether one node is less than another.
- ///
- /// The first node to compare.
- /// The second node to compare.
- /// True if is less than ; otherwise, false.
- public static bool operator <(HierarchyId hid1, HierarchyId hid2)
- {
- var sh1 = Unwrap(hid1);
- var sh2 = Unwrap(hid2);
+ ///
+ /// Evaluates whether two nodes are equal.
+ ///
+ /// The first node to compare.
+ /// The second node to compare.
+ /// True if and are equal; otherwise, false.
+ 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;
- }
+ return sh1.IsNull == sh2.IsNull && sh1.CompareTo(sh2) == 0;
+ }
- ///
- /// Evaluates whether one node is greater than another.
- ///
- /// The first node to compare.
- /// The second node to compare.
- /// True if is greater than ; otherwise, false.
- public static bool operator >(HierarchyId hid1, HierarchyId hid2)
- {
- var sh1 = Unwrap(hid1);
- var sh2 = Unwrap(hid2);
+ ///
+ /// Evaluates whether two nodes are unequal.
+ ///
+ /// The first node to compare.
+ /// The second node to compare.
+ /// True if and are unequal; otherwise, false.
+ 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;
- }
+ return sh1.IsNull != sh2.IsNull || sh1.CompareTo(sh2) != 0;
+ }
- ///
- /// Evaluates whether one node is less than or equal to another.
- ///
- /// The first node to compare.
- /// The second node to compare.
- /// True if is less than or equal to ; otherwise, false.
- public static bool operator <=(HierarchyId hid1, HierarchyId hid2)
- {
- var sh1 = Unwrap(hid1);
- var sh2 = Unwrap(hid2);
+ ///
+ /// Evaluates whether one node is less than another.
+ ///
+ /// The first node to compare.
+ /// The second node to compare.
+ /// True if is less than ; otherwise, false.
+ 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;
- }
+ return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) < 0;
+ }
- ///
- /// Evaluates whether one node is greater than or equal to another.
- ///
- /// The first node to compare.
- /// The second node to compare.
- /// True if is greater than or equal to ; otherwise, false.
- public static bool operator >=(HierarchyId hid1, HierarchyId hid2)
- {
- var sh1 = Unwrap(hid1);
- var sh2 = Unwrap(hid2);
+ ///
+ /// Evaluates whether one node is greater than another.
+ ///
+ /// The first node to compare.
+ /// The second node to compare.
+ /// True if is greater than ; otherwise, false.
+ 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;
- }
+ return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) > 0;
+ }
- private static SqlHierarchyId Unwrap(HierarchyId value)
- => value?._value ?? SqlHierarchyId.Null;
+ ///
+ /// Evaluates whether one node is less than or equal to another.
+ ///
+ /// The first node to compare.
+ /// The second node to compare.
+ /// True if is less than or equal to ; otherwise, false.
+ public static bool operator <=(HierarchyId? hid1, HierarchyId? hid2)
+ {
+ var sh1 = Unwrap(hid1);
+ var sh2 = Unwrap(hid2);
- private static HierarchyId Wrap(SqlHierarchyId value)
- => value.IsNull ? null : new HierarchyId(value);
+ return !sh1.IsNull && !sh2.IsNull && sh1.CompareTo(sh2) <= 0;
}
-}
\ No newline at end of file
+
+ ///
+ /// Evaluates whether one node is greater than or equal to another.
+ ///
+ /// The first node to compare.
+ /// The second node to compare.
+ /// True if is greater than or equal to ; otherwise, false.
+ 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);
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Design/Internal/SqlServerHierarchyIdDesignTimeServices.cs b/src/EFCore.SqlServer.HierarchyId/Design/Internal/SqlServerHierarchyIdDesignTimeServices.cs
new file mode 100644
index 00000000000..de4bb6922c3
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Design/Internal/SqlServerHierarchyIdDesignTimeServices.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.EntityFrameworkCore.Scaffolding;
+using Microsoft.EntityFrameworkCore.SqlServer.Scaffolding.Internal;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Design.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdDesignTimeServices : IDesignTimeServices
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
+ {
+ serviceCollection
+ .AddSingleton()
+ .AddSingleton();
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Design/SqlServerHierarchyIdDesignTimeServices.cs b/src/EFCore.SqlServer.HierarchyId/Design/SqlServerHierarchyIdDesignTimeServices.cs
deleted file mode 100644
index 9cfe4fbb710..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Design/SqlServerHierarchyIdDesignTimeServices.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-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
-{
- ///
- /// Enables configuring design-time services. Tools will automatically discover implementations of this
- /// interface that are in the startup assembly.
- ///
- public class SqlServerHierarchyIdDesignTimeServices : IDesignTimeServices
- {
- ///
- /// Configures design-time services. Use this method to override the default design-time services with your
- /// own implementations.
- ///
- /// The design-time service collection.
- public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
- {
- serviceCollection
- .AddSingleton()
- .AddSingleton();
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/EFCore.SqlServer.HierarchyId.csproj b/src/EFCore.SqlServer.HierarchyId/EFCore.SqlServer.HierarchyId.csproj
index 5fe27346456..fad2f7a53c6 100644
--- a/src/EFCore.SqlServer.HierarchyId/EFCore.SqlServer.HierarchyId.csproj
+++ b/src/EFCore.SqlServer.HierarchyId/EFCore.SqlServer.HierarchyId.csproj
@@ -2,13 +2,25 @@
net6.0
- EntityFrameworkCore.SqlServer.HierarchyId
+ 3.6
+ Microsoft.EntityFrameworkCore.SqlServer.HierarchyId
Microsoft.EntityFrameworkCore.SqlServer
Adds hierarchyid support to the SQL Server EF Core provider
- true
true
+ $(PackageTags);SQL Server;HierarchyId
+ true
+
+
+
+
+
+
+
+
+
+
True
@@ -17,26 +29,29 @@
-
+
+
+
-
+
+ TextTemplatingFileGenerator
+ SqlServerHierarchyIdStrings.Designer.cs
+ Microsoft.EntityFrameworkCore.SqlServer.Internal
+
-
- True
- True
- Resources.resx
-
+
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
+
+ True
+ True
+ SqlServerHierarchyIdStrings.Designer.tt
+
diff --git a/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdDbContextOptionsBuilderExtensions.cs b/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdDbContextOptionsBuilderExtensions.cs
index d4f926e8623..74c21901203 100644
--- a/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdDbContextOptionsBuilderExtensions.cs
+++ b/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdDbContextOptionsBuilderExtensions.cs
@@ -1,29 +1,31 @@
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.EntityFrameworkCore
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// HierarchyId specific extension methods for .
+///
+public static class SqlServerHierarchyIdDbContextOptionsBuilderExtensions
{
///
- /// HierarchyId specific extension methods for .
+ /// Enable HierarchyId mappings.
///
- public static class SqlServerHierarchyIdDbContextOptionsBuilderExtensions
+ /// The builder being used to configure SQL Server.
+ /// The options builder so that further configuration can be chained.
+ public static SqlServerDbContextOptionsBuilder UseHierarchyId(
+ this SqlServerDbContextOptionsBuilder optionsBuilder)
{
- ///
- /// Enable HierarchyId mappings.
- ///
- /// The builder being used to configure SQL Server.
- /// The options builder so that further configuration can be chained.
- public static SqlServerDbContextOptionsBuilder UseHierarchyId(
- this SqlServerDbContextOptionsBuilder optionsBuilder)
- {
- var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
+ var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
- var extension = coreOptionsBuilder.Options.FindExtension()
- ?? new SqlServerHierarchyIdOptionsExtension();
+ var extension = coreOptionsBuilder.Options.FindExtension()
+ ?? new SqlServerHierarchyIdOptionsExtension();
- ((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);
+ ((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);
- return optionsBuilder;
- }
+ return optionsBuilder;
}
}
diff --git a/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdServiceCollectionExtensions.cs b/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdServiceCollectionExtensions.cs
index 77df6399117..5d4b3e2dc9a 100644
--- a/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdServiceCollectionExtensions.cs
+++ b/src/EFCore.SqlServer.HierarchyId/Extensions/SqlServerHierarchyIdServiceCollectionExtensions.cs
@@ -1,29 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
-using Microsoft.EntityFrameworkCore.SqlServer.Query.ExpressionTranslators;
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
+using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage;
-namespace Microsoft.Extensions.DependencyInjection
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// EntityFrameworkCore.SqlServer.HierarchyId extension methods for .
+///
+public static class SqlServerHierarchyIdServiceCollectionExtensions
{
///
- /// EntityFrameworkCore.SqlServer.HierarchyId extension methods for .
+ /// Adds the services required for HierarchyId support in the SQL Server provider for Entity Framework.
///
- public static class SqlServerHierarchyIdServiceCollectionExtensions
+ /// The to add services to.
+ /// The same service collection so that multiple calls can be chained.
+ public static IServiceCollection AddEntityFrameworkSqlServerHierarchyId(
+ this IServiceCollection serviceCollection)
{
- ///
- /// Adds the services required for HierarchyId support in the SQL Server provider for Entity Framework.
- ///
- /// The to add services to.
- /// The same service collection so that multiple calls can be chained.
- public static IServiceCollection AddEntityFrameworkSqlServerHierarchyId(
- this IServiceCollection serviceCollection)
- {
- new EntityFrameworkRelationalServicesBuilder(serviceCollection)
- .TryAdd()
- .TryAdd();
+ new EntityFrameworkRelationalServicesBuilder(serviceCollection)
+ .TryAdd()
+ .TryAdd();
- return serviceCollection;
- }
+ return serviceCollection;
}
}
diff --git a/src/EFCore.SqlServer.HierarchyId/Infrastructure/Internal/SqlServerHierarchyIdOptionsExtension.cs b/src/EFCore.SqlServer.HierarchyId/Infrastructure/Internal/SqlServerHierarchyIdOptionsExtension.cs
new file mode 100644
index 00000000000..47905aa9f5c
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Infrastructure/Internal/SqlServerHierarchyIdOptionsExtension.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.SqlServer.Internal;
+using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdOptionsExtension : IDbContextOptionsExtension
+{
+ private DbContextOptionsExtensionInfo? _info;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public DbContextOptionsExtensionInfo Info
+ => _info ??= new ExtensionInfo(this);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ApplyServices(IServiceCollection services)
+ {
+ services.AddEntityFrameworkSqlServerHierarchyId();
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void Validate(IDbContextOptions options)
+ {
+ var internalServiceProvider = options.FindExtension()?.InternalServiceProvider;
+ if (internalServiceProvider != null)
+ {
+ using (var scope = internalServiceProvider.CreateScope())
+ {
+ if (scope.ServiceProvider.GetService>()
+ ?.Any(s => s is SqlServerHierarchyIdMethodCallTranslatorPlugin)
+ != true
+ || scope.ServiceProvider.GetService>()
+ ?.Any(s => s is SqlServerHierarchyIdTypeMappingSourcePlugin)
+ != true)
+ {
+ throw new InvalidOperationException(SqlServerHierarchyIdStrings.ServicesMissing);
+ }
+ }
+ }
+ }
+
+ private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
+ {
+ public ExtensionInfo(IDbContextOptionsExtension extension)
+ : base(extension)
+ {
+ }
+
+ private new SqlServerHierarchyIdOptionsExtension Extension
+ => (SqlServerHierarchyIdOptionsExtension)base.Extension;
+
+ public override bool IsDatabaseProvider
+ => false;
+
+ public override int GetServiceProviderHashCode()
+ => 0;
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ => other is ExtensionInfo;
+
+ public override void PopulateDebugInfo(IDictionary debugInfo)
+ => debugInfo["SqlServer:" + nameof(SqlServerHierarchyIdDbContextOptionsBuilderExtensions.UseHierarchyId)] = "1";
+
+ public override string LogFragment
+ => "using HierarchyId ";
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Infrastructure/SqlServerHierarchyIdOptionsExtension.cs b/src/EFCore.SqlServer.HierarchyId/Infrastructure/SqlServerHierarchyIdOptionsExtension.cs
deleted file mode 100644
index 1fdbd5265cc..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Infrastructure/SqlServerHierarchyIdOptionsExtension.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Query;
-using Microsoft.EntityFrameworkCore.SqlServer.Properties;
-using Microsoft.EntityFrameworkCore.SqlServer.Query.ExpressionTranslators;
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
-using Microsoft.EntityFrameworkCore.Storage;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure
-{
- internal class SqlServerHierarchyIdOptionsExtension : IDbContextOptionsExtension
- {
- private DbContextOptionsExtensionInfo _info;
-
- public DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
-
- public virtual void ApplyServices(IServiceCollection services)
- {
- services.AddEntityFrameworkSqlServerHierarchyId();
- }
-
- public virtual void Validate(IDbContextOptions options)
- {
- var internalServiceProvider = options.FindExtension()?.InternalServiceProvider;
- if (internalServiceProvider != null)
- {
- using (var scope = internalServiceProvider.CreateScope())
- {
- if (scope.ServiceProvider.GetService>()
- ?.Any(s => s is SqlServerHierarchyIdMethodCallTranslatorPlugin) != true ||
- scope.ServiceProvider.GetService>()
- ?.Any(s => s is SqlServerHierarchyIdTypeMappingSourcePlugin) != true)
- {
- throw new InvalidOperationException(Resources.ServicesMissing);
- }
- }
- }
- }
-
- private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
- {
- public ExtensionInfo(IDbContextOptionsExtension extension)
- : base(extension)
- {
- }
-
- private new SqlServerHierarchyIdOptionsExtension Extension
- => (SqlServerHierarchyIdOptionsExtension)base.Extension;
-
- public override bool IsDatabaseProvider => false;
-
- public override int GetServiceProviderHashCode() => 0;
-
- public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
- => other is ExtensionInfo;
-
- public override void PopulateDebugInfo(IDictionary debugInfo)
- => debugInfo["SqlServer:" + nameof(SqlServerHierarchyIdDbContextOptionsBuilderExtensions.UseHierarchyId)] = "1";
-
- public override string LogFragment => "using HierarchyId ";
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Properties/InternalsVisibleTo.cs b/src/EFCore.SqlServer.HierarchyId/Properties/InternalsVisibleTo.cs
deleted file mode 100644
index 5fd03cebc42..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Properties/InternalsVisibleTo.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo(
- "EntityFrameworkCore.SqlServer.HierarchyId.Test, PublicKey=00240000048000009400000006020000002400005253413100040000010001006d92138307b5e251bf4918cf751dc83489f4b2d70ac15b04110fd1f78491fe93719d0cd464d103a95fb1c3b1cb21ce0033c94c6f52b325d36360736dea7571bd1074cb2c937cf4fc54526ceb44271c4f44753dbeb5d9b364e4dc57a8988542d3a7edb6575bc35ce7670612bd8f00f2c6899f3e74bd563810fa45f4c5c8b51cd3")]
diff --git a/src/EFCore.SqlServer.HierarchyId/Properties/Resources.Designer.cs b/src/EFCore.SqlServer.HierarchyId/Properties/Resources.Designer.cs
deleted file mode 100644
index 2dd653ff2b1..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Properties {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.EntityFrameworkCore.SqlServer.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to UseHierarchyId requires AddEntityFrameworkSqlServerHierarchyId to be called on the internal service provider used..
- ///
- internal static string ServicesMissing {
- get {
- return ResourceManager.GetString("ServicesMissing", resourceCulture);
- }
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.Designer.cs b/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.Designer.cs
new file mode 100644
index 00000000000..1d2fc122f36
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.Designer.cs
@@ -0,0 +1,40 @@
+//
+
+using System;
+using System.Reflection;
+using System.Resources;
+
+#nullable enable
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Internal
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public static class SqlServerHierarchyIdStrings
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.EntityFrameworkCore.SqlServer.Properties.SqlServerHierarchyIdStrings", typeof(SqlServerHierarchyIdStrings).Assembly);
+
+ ///
+ /// UseHierarchyId requires AddEntityFrameworkSqlServerHierarchyId to be called on the internal service provider used.
+ ///
+ public static string ServicesMissing
+ => GetString("ServicesMissing");
+
+ private static string GetString(string name, params string[] formatterNames)
+ {
+ var value = _resourceManager.GetString(name)!;
+ for (var i = 0; i < formatterNames.Length; i++)
+ {
+ value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
+ }
+
+ return value;
+ }
+ }
+}
+
diff --git a/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.Designer.tt b/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.Designer.tt
new file mode 100644
index 00000000000..3946db3154a
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.Designer.tt
@@ -0,0 +1,7 @@
+<#
+ Session["ResourceFile"] = "SqlServerHierarchyIdStrings.resx";
+ Session["ResourceNamespace"] = "Microsoft.EntityFrameworkCore.SqlServer.Properties";
+ Session["LoggingDefinitionsClass"] = "SqlServerHierarchyIdLoggingDefinitions";
+ Session["NoDiagnostics"] = true;
+#>
+<#@ include file="..\..\..\tools\Resources.tt" #>
\ No newline at end of file
diff --git a/src/EFCore.SqlServer.HierarchyId/Properties/Resources.resx b/src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.resx
similarity index 100%
rename from src/EFCore.SqlServer.HierarchyId/Properties/Resources.resx
rename to src/EFCore.SqlServer.HierarchyId/Properties/SqlServerHierarchyIdStrings.resx
diff --git a/src/EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs b/src/EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs
deleted file mode 100644
index 92ff81eaefa..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-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 Translators { get; }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodTranslator.cs b/src/EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodTranslator.cs
deleted file mode 100644
index 0b2af7439a6..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Query/ExpressionTranslators/SqlServerHierarchyIdMethodTranslator.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Query;
-using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
-using Microsoft.EntityFrameworkCore.Storage;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Query.ExpressionTranslators
-{
- internal class SqlServerHierarchyIdMethodTranslator : IMethodCallTranslator
- {
- private static readonly IDictionary _methodToFunctionName = new Dictionary
- {
- // instance methods
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetAncestor), new[] { typeof(int) }), "GetAncestor" },
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetDescendant), new[] { typeof(HierarchyId), typeof(HierarchyId) }), "GetDescendant" },
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetLevel), Type.EmptyTypes), "GetLevel" },
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetReparentedValue), new[] { typeof(HierarchyId), typeof(HierarchyId) }), "GetReparentedValue" },
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.IsDescendantOf), new[] { typeof(HierarchyId) }), "IsDescendantOf" },
- { typeof(object).GetRuntimeMethod(nameof(HierarchyId.ToString), Type.EmptyTypes), "ToString" },
-
- // static methods
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetRoot), Type.EmptyTypes), "hierarchyid::GetRoot" },
- { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.Parse), new[] { typeof(string) }), "hierarchyid::Parse" },
- };
-
- private readonly IRelationalTypeMappingSource _typeMappingSource;
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
-
- public SqlServerHierarchyIdMethodTranslator(
- IRelationalTypeMappingSource typeMappingSource,
- ISqlExpressionFactory sqlExpressionFactory)
- {
- _typeMappingSource = typeMappingSource;
- _sqlExpressionFactory = sqlExpressionFactory;
- }
-
- public SqlExpression Translate(
- SqlExpression instance,
- MethodInfo method,
- IReadOnlyList arguments,
- IDiagnosticsLogger logger)
- {
- // instance is null for static methods like Parse
- const string storeType = SqlServerHierarchyIdTypeMappingSourcePlugin.SqlServerTypeName;
- var callingType = instance?.Type ?? method.DeclaringType;
- if (typeof(HierarchyId).IsAssignableFrom(callingType)
- && _methodToFunctionName.TryGetValue(method, out var functionName))
- {
- var typeMappedArguments = new List();
- foreach (var argument in arguments)
- {
- var argumentTypeMapping = typeof(HierarchyId).IsAssignableFrom(argument.Type)
- ? _typeMappingSource.FindMapping(argument.Type, storeType)
- : _typeMappingSource.FindMapping(argument.Type);
- var mappedArgument = _sqlExpressionFactory.ApplyTypeMapping(argument, argumentTypeMapping);
- typeMappedArguments.Add(mappedArgument);
- }
-
- var resultTypeMapping = typeof(HierarchyId).IsAssignableFrom(method.ReturnType)
- ? _typeMappingSource.FindMapping(method.ReturnType, storeType)
- : _typeMappingSource.FindMapping(method.ReturnType);
-
-
- if (instance != null)
- {
- var instanceMapping = _typeMappingSource.FindMapping(instance.Type, storeType);
- instance = _sqlExpressionFactory.ApplyTypeMapping(instance, instanceMapping);
-
- return _sqlExpressionFactory.Function(
- instance,
- functionName,
- simplify(arguments),
- nullable: true,
- instancePropagatesNullability: true,
- argumentsPropagateNullability: arguments.Select(a => true),
- method.ReturnType,
- resultTypeMapping);
- }
-
- return _sqlExpressionFactory.Function(
- functionName,
- simplify(arguments),
- nullable: true,
- argumentsPropagateNullability: arguments.Select(a => true),
- method.ReturnType,
- resultTypeMapping);
- }
-
- return null;
- }
-
- private IEnumerable simplify(IEnumerable arguments)
- {
- foreach (var argument in arguments)
- {
- if (argument is SqlConstantExpression constant
- && constant.Value is HierarchyId hierarchyId)
- {
- yield return _sqlExpressionFactory.Fragment($"'{hierarchyId}'");
- }
- else
- {
- yield return argument;
- }
- }
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Query/Internal/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs b/src/EFCore.SqlServer.HierarchyId/Query/Internal/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs
new file mode 100644
index 00000000000..712bb58671d
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Query/Internal/SqlServerHierarchyIdMethodCallTranslatorPlugin.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public SqlServerHierarchyIdMethodCallTranslatorPlugin(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ Translators = new IMethodCallTranslator[] { new SqlServerHierarchyIdMethodTranslator(typeMappingSource, sqlExpressionFactory) };
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Query/Internal/SqlServerHierarchyIdMethodTranslator.cs b/src/EFCore.SqlServer.HierarchyId/Query/Internal/SqlServerHierarchyIdMethodTranslator.cs
new file mode 100644
index 00000000000..3c697523fac
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Query/Internal/SqlServerHierarchyIdMethodTranslator.cs
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdMethodTranslator : IMethodCallTranslator
+{
+ private static readonly IDictionary _methodToFunctionName = new Dictionary
+ {
+ // instance methods
+ { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetAncestor), new[] { typeof(int) })!, "GetAncestor" },
+ {
+ typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetDescendant), new[] { typeof(HierarchyId), typeof(HierarchyId) })!,
+ "GetDescendant"
+ },
+ { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetLevel), Type.EmptyTypes)!, "GetLevel" },
+ {
+ typeof(HierarchyId).GetRuntimeMethod(
+ nameof(HierarchyId.GetReparentedValue), new[] { typeof(HierarchyId), typeof(HierarchyId) })!,
+ "GetReparentedValue"
+ },
+ { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.IsDescendantOf), new[] { typeof(HierarchyId) })!, "IsDescendantOf" },
+ { typeof(object).GetRuntimeMethod(nameof(HierarchyId.ToString), Type.EmptyTypes)!, "ToString" },
+
+ // static methods
+ { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.GetRoot), Type.EmptyTypes)!, "hierarchyid::GetRoot" },
+ { typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.Parse), new[] { typeof(string) })!, "hierarchyid::Parse" },
+ };
+
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public SqlServerHierarchyIdMethodTranslator(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _typeMappingSource = typeMappingSource;
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ // instance is null for static methods like Parse
+ const string storeType = SqlServerHierarchyIdTypeMappingSourcePlugin.SqlServerTypeName;
+ var callingType = instance?.Type ?? method.DeclaringType;
+ if (typeof(HierarchyId).IsAssignableFrom(callingType)
+ && _methodToFunctionName.TryGetValue(method, out var functionName))
+ {
+ var typeMappedArguments = new List();
+ foreach (var argument in arguments)
+ {
+ var argumentTypeMapping = typeof(HierarchyId).IsAssignableFrom(argument.Type)
+ ? _typeMappingSource.FindMapping(argument.Type, storeType)
+ : _typeMappingSource.FindMapping(argument.Type);
+ var mappedArgument = _sqlExpressionFactory.ApplyTypeMapping(argument, argumentTypeMapping);
+ typeMappedArguments.Add(mappedArgument);
+ }
+
+ var resultTypeMapping = typeof(HierarchyId).IsAssignableFrom(method.ReturnType)
+ ? _typeMappingSource.FindMapping(method.ReturnType, storeType)
+ : _typeMappingSource.FindMapping(method.ReturnType);
+
+ if (instance != null)
+ {
+ var instanceMapping = _typeMappingSource.FindMapping(instance.Type, storeType);
+ instance = _sqlExpressionFactory.ApplyTypeMapping(instance, instanceMapping);
+
+ return _sqlExpressionFactory.Function(
+ instance,
+ functionName,
+ Simplify(arguments),
+ nullable: true,
+ instancePropagatesNullability: true,
+ argumentsPropagateNullability: arguments.Select(a => true),
+ method.ReturnType,
+ resultTypeMapping);
+ }
+
+ return _sqlExpressionFactory.Function(
+ functionName,
+ Simplify(arguments),
+ nullable: true,
+ argumentsPropagateNullability: arguments.Select(a => true),
+ method.ReturnType,
+ resultTypeMapping);
+ }
+
+ return null;
+ }
+
+ private IEnumerable Simplify(IEnumerable arguments)
+ {
+ foreach (var argument in arguments)
+ {
+ if (argument is SqlConstantExpression constant
+ && constant.Value is HierarchyId hierarchyId)
+ {
+ yield return _sqlExpressionFactory.Fragment($"'{hierarchyId}'");
+ }
+ else
+ {
+ yield return argument;
+ }
+ }
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Scaffolding/Internal/SqlServerHierarchyIdCodeGeneratorPlugin.cs b/src/EFCore.SqlServer.HierarchyId/Scaffolding/Internal/SqlServerHierarchyIdCodeGeneratorPlugin.cs
new file mode 100644
index 00000000000..4d4667064d5
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Scaffolding/Internal/SqlServerHierarchyIdCodeGeneratorPlugin.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Scaffolding;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Scaffolding.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdCodeGeneratorPlugin : ProviderCodeGeneratorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override MethodCallCodeFragment GenerateProviderOptions()
+ {
+ return new MethodCallCodeFragment(
+ typeof(SqlServerHierarchyIdDbContextOptionsBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerHierarchyIdDbContextOptionsBuilderExtensions.UseHierarchyId),
+ new[] { typeof(SqlServerDbContextOptionsBuilder) })!);
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Scaffolding/SqlServerHierarchyIdCodeGeneratorPlugin.cs b/src/EFCore.SqlServer.HierarchyId/Scaffolding/SqlServerHierarchyIdCodeGeneratorPlugin.cs
deleted file mode 100644
index 899f13290b5..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Scaffolding/SqlServerHierarchyIdCodeGeneratorPlugin.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Design;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Scaffolding;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Scaffolding
-{
- internal class SqlServerHierarchyIdCodeGeneratorPlugin : ProviderCodeGeneratorPlugin
- {
- public override MethodCallCodeFragment GenerateProviderOptions()
- {
- return new MethodCallCodeFragment(
- typeof(SqlServerHierarchyIdDbContextOptionsBuilderExtensions).GetRuntimeMethod(
- nameof(SqlServerHierarchyIdDbContextOptionsBuilderExtensions.UseHierarchyId),
- new[] { typeof(SqlServerDbContextOptionsBuilder) }));
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs
new file mode 100644
index 00000000000..d346691b106
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs
@@ -0,0 +1,219 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Data;
+using System.Data.Common;
+using System.Data.SqlTypes;
+using System.Linq.Expressions;
+using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.ValueConversion.Internal;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdTypeMapping : RelationalTypeMapping
+{
+ private static readonly MethodInfo _getSqlBytes
+ = typeof(SqlDataReader).GetRuntimeMethod(nameof(SqlDataReader.GetSqlBytes), new[] { typeof(int) })!;
+
+ private static readonly MethodInfo _parseHierarchyId
+ = typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.Parse), new[] { typeof(string) })!;
+
+ private static readonly SqlServerHierarchyIdValueConverter _valueConverter = new();
+
+ private static Action? _sqlDbTypeSetter;
+ private static Action? _udtTypeNameSetter;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public SqlServerHierarchyIdTypeMapping(string storeType, Type clrType)
+ : base(CreateRelationalTypeMappingParameters(storeType, clrType))
+ {
+ }
+
+ private static RelationalTypeMappingParameters CreateRelationalTypeMappingParameters(string storeType, Type clrType)
+ {
+ return new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(
+ clrType: clrType,
+ converter: null //this gets the generatecodeliteral to run
+ ),
+ storeType);
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ // needed to implement Clone
+ protected SqlServerHierarchyIdTypeMapping(RelationalTypeMappingParameters parameters)
+ : base(parameters)
+ {
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ {
+ return new SqlServerHierarchyIdTypeMapping(parameters);
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ var type = parameter.GetType();
+ LazyInitializer.EnsureInitialized(ref _sqlDbTypeSetter, () => CreateSqlDbTypeAccessor(type));
+ LazyInitializer.EnsureInitialized(ref _udtTypeNameSetter, () => CreateUdtTypeNameAccessor(type));
+
+ if (parameter.Value == DBNull.Value)
+ {
+ parameter.Value = SqlBytes.Null;
+ }
+
+ _sqlDbTypeSetter(parameter, SqlDbType.Udt);
+ _udtTypeNameSetter(parameter, StoreType);
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override MethodInfo GetDataReaderMethod()
+ {
+ return _getSqlBytes;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override Expression GenerateCodeLiteral(object value)
+ {
+ return Expression.Call(
+ _parseHierarchyId,
+ Expression.Constant(value.ToString(), typeof(string))
+ );
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override string GenerateNonNullSqlLiteral(object value)
+ {
+ //this appears to only be called when using the update-database
+ //command, and the value is already a hierarchyid
+ return $"'{value}'";
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override DbParameter CreateParameter(
+ DbCommand command,
+ string name,
+ object? value,
+ bool? nullable = null,
+ ParameterDirection direction = ParameterDirection.Input)
+ {
+ var parameter = command.CreateParameter();
+ parameter.Direction = ParameterDirection.Input;
+ parameter.ParameterName = name;
+
+ if (Converter != null)
+ {
+ value = Converter.ConvertToProvider(value);
+ }
+
+ parameter.Value = value is null
+ ? DBNull.Value
+ : _valueConverter.ConvertToProvider(value);
+
+ if (nullable.HasValue)
+ {
+ parameter.IsNullable = nullable.Value;
+ }
+
+ ConfigureParameter(parameter);
+
+ return parameter;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override Expression CustomizeDataReaderExpression(Expression expression)
+ {
+ if (expression.Type != _valueConverter.ProviderClrType)
+ {
+ expression = Expression.Convert(expression, _valueConverter.ProviderClrType);
+ }
+
+ return ReplacingExpressionVisitor.Replace(
+ _valueConverter.ConvertFromProviderExpression.Parameters.Single(),
+ expression,
+ _valueConverter.ConvertFromProviderExpression.Body);
+ }
+
+ private static Action CreateSqlDbTypeAccessor(Type paramType)
+ {
+ var paramParam = Expression.Parameter(typeof(DbParameter), "parameter");
+ var valueParam = Expression.Parameter(typeof(SqlDbType), "value");
+
+ return Expression.Lambda>(
+ Expression.Call(
+ Expression.Convert(paramParam, paramType),
+ paramType.GetProperty("SqlDbType")!.SetMethod!,
+ valueParam),
+ paramParam,
+ valueParam).Compile();
+ }
+
+ private static Action CreateUdtTypeNameAccessor(Type paramType)
+ {
+ var paramParam = Expression.Parameter(typeof(DbParameter), "parameter");
+ var valueParam = Expression.Parameter(typeof(string), "value");
+
+ return Expression.Lambda>(
+ Expression.Call(
+ Expression.Convert(paramParam, paramType),
+ paramType.GetProperty("UdtTypeName")!.SetMethod!,
+ valueParam),
+ paramParam,
+ valueParam).Compile();
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
new file mode 100644
index 00000000000..b2fb980a0b7
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public const string SqlServerTypeName = "hierarchyid";
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo)
+ {
+ var clrType = mappingInfo.ClrType;
+ var storeTypeName = mappingInfo.StoreTypeName;
+
+ return typeof(HierarchyId).IsAssignableFrom(clrType)
+ || SqlServerTypeName.Equals(storeTypeName, StringComparison.OrdinalIgnoreCase)
+ ? new SqlServerHierarchyIdTypeMapping(SqlServerTypeName, clrType ?? typeof(HierarchyId))
+ : null;
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdTypeMapping.cs b/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdTypeMapping.cs
deleted file mode 100644
index 4c08f5d2264..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdTypeMapping.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-using System;
-using System.Data;
-using System.Data.Common;
-using System.Data.SqlTypes;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using System.Threading;
-using Microsoft.Data.SqlClient;
-using Microsoft.EntityFrameworkCore.Query;
-using Microsoft.EntityFrameworkCore.Storage;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Storage
-{
- internal class SqlServerHierarchyIdTypeMapping : RelationalTypeMapping
- {
- private static readonly MethodInfo _getSqlBytes
- = typeof(SqlDataReader).GetRuntimeMethod(nameof(SqlDataReader.GetSqlBytes), new[] { typeof(int) });
-
- private static readonly MethodInfo _parseHierarchyId
- = typeof(HierarchyId).GetRuntimeMethod(nameof(HierarchyId.Parse), new[] { typeof(string) });
-
- private static readonly SqlServerHierarchyIdValueConverter _valueConverter = new SqlServerHierarchyIdValueConverter();
-
- private static Action _sqlDbTypeSetter;
- private static Action _udtTypeNameSetter;
-
- public SqlServerHierarchyIdTypeMapping(string storeType, Type clrType)
- : base(CreateRelationalTypeMappingParameters(storeType, clrType))
- {
- }
-
- private static RelationalTypeMappingParameters CreateRelationalTypeMappingParameters(string storeType, Type clrType)
- {
- return new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(
- clrType: clrType,
- converter: null //this gets the generatecodeliteral to run
- ),
- storeType);
- }
-
- // needed to implement Clone
- protected SqlServerHierarchyIdTypeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters)
- {
- }
-
- protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
- {
- return new SqlServerHierarchyIdTypeMapping(parameters);
- }
-
- protected override void ConfigureParameter(DbParameter parameter)
- {
- var type = parameter.GetType();
- LazyInitializer.EnsureInitialized(ref _sqlDbTypeSetter, () => CreateSqlDbTypeAccessor(type));
- LazyInitializer.EnsureInitialized(ref _udtTypeNameSetter, () => CreateUdtTypeNameAccessor(type));
-
- if (parameter.Value == DBNull.Value)
- {
- parameter.Value = SqlBytes.Null;
- }
-
- _sqlDbTypeSetter(parameter, SqlDbType.Udt);
- _udtTypeNameSetter(parameter, StoreType);
- }
-
- public override MethodInfo GetDataReaderMethod()
- {
- return _getSqlBytes;
- }
-
- public override Expression GenerateCodeLiteral(object value)
- {
- return Expression.Call(
- _parseHierarchyId,
- Expression.Constant(value.ToString(), typeof(string))
- );
- }
-
- protected override string GenerateNonNullSqlLiteral(object value)
- {
- //this appears to only be called when using the update-database
- //command, and the value is already a hierarchyid
- return $"'{value}'";
- }
-
- public override DbParameter CreateParameter(DbCommand command, string name, object value, bool? nullable = null, ParameterDirection direction = ParameterDirection.Input)
- {
- var parameter = command.CreateParameter();
- parameter.Direction = ParameterDirection.Input;
- parameter.ParameterName = name;
-
- if (Converter != null)
- {
- value = Converter.ConvertToProvider(value);
- }
-
- parameter.Value = value is null
- ? DBNull.Value
- : _valueConverter.ConvertToProvider(value);
-
- if (nullable.HasValue)
- {
- parameter.IsNullable = nullable.Value;
- }
-
- ConfigureParameter(parameter);
-
- return parameter;
- }
-
- public override Expression CustomizeDataReaderExpression(Expression expression)
- {
- if (expression.Type != _valueConverter.ProviderClrType)
- {
- expression = Expression.Convert(expression, _valueConverter.ProviderClrType);
- }
-
- return ReplacingExpressionVisitor.Replace(
- _valueConverter.ConvertFromProviderExpression.Parameters.Single(),
- expression,
- _valueConverter.ConvertFromProviderExpression.Body);
- }
-
- private static Action CreateSqlDbTypeAccessor(Type paramType)
- {
- var paramParam = Expression.Parameter(typeof(DbParameter), "parameter");
- var valueParam = Expression.Parameter(typeof(SqlDbType), "value");
-
- return Expression.Lambda>(
- Expression.Call(
- Expression.Convert(paramParam, paramType),
- paramType.GetProperty("SqlDbType").SetMethod,
- valueParam),
- paramParam,
- valueParam).Compile();
- }
-
- private static Action CreateUdtTypeNameAccessor(Type paramType)
- {
- var paramParam = Expression.Parameter(typeof(DbParameter), "parameter");
- var valueParam = Expression.Parameter(typeof(string), "value");
-
- return Expression.Lambda>(
- Expression.Call(
- Expression.Convert(paramParam, paramType),
- paramType.GetProperty("UdtTypeName").SetMethod,
- valueParam),
- paramParam,
- valueParam).Compile();
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdTypeMappingSourcePlugin.cs b/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
deleted file mode 100644
index 10e54dd2189..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Storage;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Storage
-{
- internal class SqlServerHierarchyIdTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
- {
- public const string SqlServerTypeName = "hierarchyid";
-
- public virtual RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
- {
- var clrType = mappingInfo.ClrType;
- var storeTypeName = mappingInfo.StoreTypeName;
-
- return typeof(HierarchyId).IsAssignableFrom(clrType)
- || SqlServerTypeName.Equals(storeTypeName, StringComparison.OrdinalIgnoreCase)
- ? new SqlServerHierarchyIdTypeMapping(SqlServerTypeName, clrType ?? typeof(HierarchyId))
- : null;
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdValueConverter.cs b/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdValueConverter.cs
deleted file mode 100644
index cd5593c4f72..00000000000
--- a/src/EFCore.SqlServer.HierarchyId/Storage/SqlServerHierarchyIdValueConverter.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Data.SqlTypes;
-using System.IO;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Storage
-{
- internal class SqlServerHierarchyIdValueConverter : ValueConverter
- {
- 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);
- }
- }
- }
-}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/ValueConversion/Internal/SqlServerHierarchyIdValueConverter.cs b/src/EFCore.SqlServer.HierarchyId/Storage/ValueConversion/Internal/SqlServerHierarchyIdValueConverter.cs
new file mode 100644
index 00000000000..7edd8eabddb
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/ValueConversion/Internal/SqlServerHierarchyIdValueConverter.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Data.SqlTypes;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.ValueConversion.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerHierarchyIdValueConverter : ValueConverter
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ 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)!;
+ }
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/build/net6.0/EntityFrameworkCore.SqlServer.HierarchyId.targets b/src/EFCore.SqlServer.HierarchyId/build/net6.0/Microsoft.EntityFrameworkCore.SqlServer.HierarchyId.targets
similarity index 94%
rename from src/EFCore.SqlServer.HierarchyId/build/net6.0/EntityFrameworkCore.SqlServer.HierarchyId.targets
rename to src/EFCore.SqlServer.HierarchyId/build/net6.0/Microsoft.EntityFrameworkCore.SqlServer.HierarchyId.targets
index 09f8c908850..b445f6e4b57 100644
--- a/src/EFCore.SqlServer.HierarchyId/build/net6.0/EntityFrameworkCore.SqlServer.HierarchyId.targets
+++ b/src/EFCore.SqlServer.HierarchyId/build/net6.0/Microsoft.EntityFrameworkCore.SqlServer.HierarchyId.targets
@@ -32,7 +32,7 @@
Outputs="$(EFCoreSqlServerHierarchyIdFile)">
- <_Parameter1>Microsoft.EntityFrameworkCore.SqlServer.Design.SqlServerHierarchyIdDesignTimeServices, EntityFrameworkCore.SqlServer.HierarchyId
+ <_Parameter1>Microsoft.EntityFrameworkCore.SqlServer.Design.Internal.SqlServerHierarchyIdDesignTimeServices, Microsoft.EntityFrameworkCore.SqlServer.HierarchyId
<_Parameter2>Microsoft.EntityFrameworkCore.SqlServer
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/CSharpDbContextGeneratorTest.cs b/test/EFCore.SqlServer.HierarchyId.Tests/CSharpDbContextGeneratorTest.cs
index dbb660d1f41..31b6636f1b9 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/CSharpDbContextGeneratorTest.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/CSharpDbContextGeneratorTest.cs
@@ -1,7 +1,7 @@
-using System;
-using Microsoft.EntityFrameworkCore.Internal;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Scaffolding;
-using Microsoft.EntityFrameworkCore.SqlServer.Internal;
using Xunit;
namespace Microsoft.EntityFrameworkCore.SqlServer;
@@ -22,7 +22,7 @@ public void Generates_context_with_UseHierarchyId()
b.Property("Name");
});
},
- new ModelCodeGenerationOptions {UseDataAnnotations = false},
+ new ModelCodeGenerationOptions { UseDataAnnotations = false },
code =>
{
AssertFileContents(
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/CSharpEntityTypeGeneratorTest.cs b/test/EFCore.SqlServer.HierarchyId.Tests/CSharpEntityTypeGeneratorTest.cs
index 4dd7005a6ca..77c33b38c74 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/CSharpEntityTypeGeneratorTest.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/CSharpEntityTypeGeneratorTest.cs
@@ -1,4 +1,6 @@
-using System.Linq;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Scaffolding;
using Xunit;
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/DesignTimeServicesTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/DesignTimeServicesTests.cs
index 65cd1c3d3a2..1b6f94f6d4a 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/DesignTimeServicesTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/DesignTimeServicesTests.cs
@@ -1,24 +1,26 @@
-using Microsoft.EntityFrameworkCore.Scaffolding;
-using Microsoft.EntityFrameworkCore.SqlServer.Design;
-using Microsoft.EntityFrameworkCore.SqlServer.Scaffolding;
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Scaffolding;
+using Microsoft.EntityFrameworkCore.SqlServer.Design.Internal;
+using Microsoft.EntityFrameworkCore.SqlServer.Scaffolding.Internal;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
-namespace Microsoft.EntityFrameworkCore.SqlServer
+namespace Microsoft.EntityFrameworkCore.SqlServer;
+
+public class DesignTimeServicesTests
{
- public class DesignTimeServicesTests
+ [ConditionalFact]
+ public void ConfigureDesignTimeServices_works()
{
- [Fact]
- public void ConfigureDesignTimeServices_works()
- {
- var serviceCollection = new ServiceCollection();
- new SqlServerHierarchyIdDesignTimeServices().ConfigureDesignTimeServices(serviceCollection);
- var serviceProvider = serviceCollection.BuildServiceProvider();
+ var serviceCollection = new ServiceCollection();
+ new SqlServerHierarchyIdDesignTimeServices().ConfigureDesignTimeServices(serviceCollection);
+ var serviceProvider = serviceCollection.BuildServiceProvider();
- Assert.IsType(serviceProvider.GetService());
- Assert.IsType(serviceProvider.GetService());
- }
+ Assert.IsType(serviceProvider.GetService());
+ Assert.IsType(serviceProvider.GetService());
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/EFCore.SqlServer.HierarchyId.Tests.csproj b/test/EFCore.SqlServer.HierarchyId.Tests/EFCore.SqlServer.HierarchyId.Tests.csproj
index 9ac69ed5adb..d976dff0a05 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/EFCore.SqlServer.HierarchyId.Tests.csproj
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/EFCore.SqlServer.HierarchyId.Tests.csproj
@@ -1,24 +1,18 @@
-
+
net7.0
- false
- EntityFrameworkCore.SqlServer.HierarchyId.Test
+ Microsoft.EntityFrameworkCore.SqlServer.HierarchyId.Tests
Microsoft.EntityFrameworkCore.SqlServer
+ True
+ true
-
-
-
-
-
-
-
+
+
-
-
-
+
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/MigrationTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/MigrationTests.cs
index deddd3596e0..6864d82fe65 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/MigrationTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/MigrationTests.cs
@@ -1,4 +1,7 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Design;
@@ -6,58 +9,57 @@
using Microsoft.Extensions.DependencyInjection;
using Xunit;
-namespace Microsoft.EntityFrameworkCore.SqlServer
+namespace Microsoft.EntityFrameworkCore.SqlServer;
+
+public class MigrationTests
{
- public class MigrationTests
+ private delegate string MigrationCodeGetter(string migrationName, string rootNamespace);
+
+ private delegate string SnapshotCodeGetter(string rootNamespace);
+
+ [ConditionalFact]
+ public void Migration_and_snapshot_generate_with_typed_array()
{
- private delegate string MigrationCodeGetter(string migrationName, string rootNamespace);
- private delegate string SnapshotCodeGetter(string rootNamespace);
-
- [Fact]
- public void Migration_and_snapshot_generate_with_typed_array()
- {
- using var db = new TypedArraySeedContext();
- ValidateMigrationAndSnapshotCode(db, db.GetExpectedMigrationCode, db.GetExpectedSnapshotCode);
- }
-
- [Fact]
- public void Migration_and_snapshot_generate_with_anonymous_array()
- {
- using var db = new AnonymousArraySeedContext();
- ValidateMigrationAndSnapshotCode(db, db.GetExpectedMigrationCode, db.GetExpectedSnapshotCode);
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "Uses internal efcore apis")]
- private static void ValidateMigrationAndSnapshotCode(
- DbContext context,
- MigrationCodeGetter migrationCodeGetter,
- SnapshotCodeGetter snapshotCodeGetter)
- {
- const string migrationName = "MyMigration";
- const string rootNamespace = "MyApp.Data";
-
- var expectedMigration = migrationCodeGetter(migrationName, rootNamespace);
- var expectedSnapshot = snapshotCodeGetter(rootNamespace);
-
- var reporter = new OperationReporter(
- new OperationReportHandler(
- m => Console.WriteLine($" error: {m}"),
- m => Console.WriteLine($" warn: {m}"),
- m => Console.WriteLine($" info: {m}"),
- m => Console.WriteLine($"verbose: {m}")));
-
- var assembly = System.Reflection.Assembly.GetExecutingAssembly();
-
- //this works because we have placed the DesignTimeServicesReferenceAttribute
- //in the test project's properties, which simulates
- //the nuget package's build target
- var migration = new DesignTimeServicesBuilder(assembly, assembly, reporter, Array.Empty())
- .Build(context)
- .GetRequiredService()
- .ScaffoldMigration(migrationName, rootNamespace);
-
- Assert.Equal(expectedMigration, migration.MigrationCode);
- Assert.Equal(expectedSnapshot, migration.SnapshotCode);
- }
+ using var db = new TypedArraySeedContext();
+ ValidateMigrationAndSnapshotCode(db, db.GetExpectedMigrationCode, db.GetExpectedSnapshotCode);
+ }
+
+ [ConditionalFact]
+ public void Migration_and_snapshot_generate_with_anonymous_array()
+ {
+ using var db = new AnonymousArraySeedContext();
+ ValidateMigrationAndSnapshotCode(db, db.GetExpectedMigrationCode, db.GetExpectedSnapshotCode);
+ }
+
+ private static void ValidateMigrationAndSnapshotCode(
+ DbContext context,
+ MigrationCodeGetter migrationCodeGetter,
+ SnapshotCodeGetter snapshotCodeGetter)
+ {
+ const string migrationName = "MyMigration";
+ const string rootNamespace = "MyApp.Data";
+
+ var expectedMigration = migrationCodeGetter(migrationName, rootNamespace);
+ var expectedSnapshot = snapshotCodeGetter(rootNamespace);
+
+ var reporter = new OperationReporter(
+ new OperationReportHandler(
+ m => Console.WriteLine($" error: {m}"),
+ m => Console.WriteLine($" warn: {m}"),
+ m => Console.WriteLine($" info: {m}"),
+ m => Console.WriteLine($"verbose: {m}")));
+
+ var assembly = Assembly.GetExecutingAssembly();
+
+ //this works because we have placed the DesignTimeServicesReferenceAttribute
+ //in the test project's properties, which simulates
+ //the nuget package's build target
+ var migration = new DesignTimeServicesBuilder(assembly, assembly, reporter, Array.Empty())
+ .Build(context)
+ .GetRequiredService()
+ .ScaffoldMigration(migrationName, rootNamespace);
+
+ Assert.Equal(expectedMigration, migration.MigrationCode, ignoreLineEndingDifferences: true);
+ Assert.Equal(expectedSnapshot, migration.SnapshotCode, ignoreLineEndingDifferences: true);
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/ModelCodeGeneratorTestBase.cs b/test/EFCore.SqlServer.HierarchyId.Tests/ModelCodeGeneratorTestBase.cs
index c1d87785da7..06174e68d0f 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/ModelCodeGeneratorTestBase.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/ModelCodeGeneratorTestBase.cs
@@ -1,4 +1,6 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding;
@@ -8,8 +10,6 @@
namespace Microsoft.EntityFrameworkCore.SqlServer;
-#pragma warning disable EF1001
-
public abstract class ModelCodeGeneratorTestBase
{
protected void Test(
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/NullabilityTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/NullabilityTests.cs
index dc717930a68..4a2069871ed 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/NullabilityTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/NullabilityTests.cs
@@ -1,80 +1,81 @@
-using System.Linq;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Xunit;
-namespace Microsoft.EntityFrameworkCore.SqlServer
+namespace Microsoft.EntityFrameworkCore.SqlServer;
+
+public class NullabilityTests
{
- public class NullabilityTests
+ [ConditionalFact]
+ public void Null_against_null()
{
- [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);
- }
+ Assert.True(null == (HierarchyId)null);
+ Assert.False(null != (HierarchyId)null);
+ Assert.False(null > (HierarchyId)null);
+ Assert.False(null >= (HierarchyId)null);
+ Assert.False(null < (HierarchyId)null);
+ Assert.False(null <= (HierarchyId)null);
+ }
- [Fact]
- public void Null_against_nonNull()
- {
- var hid = HierarchyId.GetRoot();
- Assert.False(hid == (HierarchyId)null);
- Assert.False((HierarchyId)null == hid);
+ [ConditionalFact]
+ public void Null_against_nonNull()
+ {
+ var hid = HierarchyId.GetRoot();
+ Assert.False(hid == null);
+ Assert.False(null == hid);
- Assert.True(hid != (HierarchyId)null);
- Assert.True((HierarchyId)null != hid);
+ Assert.True(hid != null);
+ Assert.True(null != hid);
- Assert.False(hid > (HierarchyId)null);
- Assert.False((HierarchyId)null > hid);
+ Assert.False(hid > null);
+ Assert.False(null > hid);
- Assert.False(hid >= (HierarchyId)null);
- Assert.False((HierarchyId)null >= hid);
+ Assert.False(hid >= null);
+ Assert.False(null >= hid);
- Assert.False(hid < (HierarchyId)null);
- Assert.False((HierarchyId)null < hid);
+ Assert.False(hid < null);
+ Assert.False(null < hid);
- Assert.False(hid <= (HierarchyId)null);
- Assert.False((HierarchyId)null <= hid);
- }
+ Assert.False(hid <= null);
+ Assert.False(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();
+ [ConditionalFact]
+ public void NullOnly_aggregates_equalTo_null()
+ {
+ var hid = (HierarchyId)null;
+ var collection = new[] { 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 == 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);
- }
+ 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();
+ [ConditionalFact]
+ public void Aggregates_including_nulls_equalTo_nonNull()
+ {
+ var hid = HierarchyId.GetRoot();
+ var collection = new[] { null, 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 == 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);
- }
+ Assert.True(hid == max);
+ Assert.True(max == hid);
+ Assert.False(hid != max);
+ Assert.False(max != hid);
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Properties/EFCoreSqlServerHierarchyId.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Properties/EFCoreSqlServerHierarchyId.cs
deleted file mode 100644
index 4b04f36c8db..00000000000
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Properties/EFCoreSqlServerHierarchyId.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-using System;
-using System.Reflection;
-
-[assembly: Microsoft.EntityFrameworkCore.Design.DesignTimeServicesReferenceAttribute("Microsoft.EntityFrameworkCore.SqlServer.Design.SqlServerHierarchyIdDesignTimeServ" +
- "ices, EntityFrameworkCore.SqlServer.HierarchyId", "Microsoft.EntityFrameworkCore.SqlServer")]
-
-// Generated by the MSBuild WriteCodeFragment class.
-
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs
index e07e3fa6676..943f0a21044 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs
@@ -1,343 +1,340 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.SqlServer.Test.Models;
+using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
-namespace Microsoft.EntityFrameworkCore.SqlServer
+namespace Microsoft.EntityFrameworkCore.SqlServer;
+
+[SqlServerConfiguredCondition]
+public class QueryTests : IDisposable
{
- public class QueryTests : IDisposable
- {
- private readonly AbrahamicContext _db;
+ private readonly AbrahamicContext _db;
- public QueryTests()
- {
- _db = new AbrahamicContext();
- _db.Database.EnsureDeleted();
- _db.Database.EnsureCreated();
- }
+ public QueryTests()
+ {
+ _db = new AbrahamicContext();
+ _db.Database.EnsureDeleted();
+ _db.Database.EnsureCreated();
+ _db.ClearSql();
+ }
- [Fact]
- public void GetLevel_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 0
- select p.Name);
+ [ConditionalFact]
+ public void GetLevel_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 0
+ select p.Name).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
+ Condense(_db.Sql));
- Assert.Equal(new[] { "Abraham" }, results);
- }
+ Assert.Equal(new[] { "Abraham" }, results);
+ }
- [Fact]
- public void IsDescendantOf_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 3
- select p.Id.IsDescendantOf(p.Id.GetAncestor(1)));
+ [ConditionalFact]
+ public void IsDescendantOf_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 3
+ select p.Id.IsDescendantOf(p.Id.GetAncestor(1))).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].IsDescendantOf([p].[Id].GetAncestor(1)) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(3 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(
+ @"SELECT [p].[Id].IsDescendantOf([p].[Id].GetAncestor(1)) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(3 AS smallint)"),
+ Condense(_db.Sql));
- Assert.All(results, b => Assert.True(b));
- }
+ Assert.All(results, b => Assert.True(b));
+ }
- [Fact]
- public void GetAncestor_0_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 0
- select p.Id.GetAncestor(0));
+ [ConditionalFact]
+ public void GetAncestor_0_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 0
+ select p.Id.GetAncestor(0)).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].GetAncestor(0) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].GetAncestor(0) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
+ Condense(_db.Sql));
- Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
- }
+ Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
+ }
- [Fact]
- public void GetAncestor_1_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 1
- select p.Id.GetAncestor(1));
+ [ConditionalFact]
+ public void GetAncestor_1_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 1
+ select p.Id.GetAncestor(1)).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].GetAncestor(1) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(1 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].GetAncestor(1) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(1 AS smallint)"),
+ Condense(_db.Sql));
- Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
- }
+ Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
+ }
- [Fact]
- public void GetAncestor_2_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 2
- select p.Id.GetAncestor(2));
+ [ConditionalFact]
+ public void GetAncestor_2_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 2
+ select p.Id.GetAncestor(2)).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].GetAncestor(2) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(2 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].GetAncestor(2) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(2 AS smallint)"),
+ Condense(_db.Sql));
- Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
- }
+ Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
+ }
- [Fact]
- public void GetAncestor_3_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 3
- select p.Id.GetAncestor(3));
+ [ConditionalFact]
+ public void GetAncestor_3_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 3
+ select p.Id.GetAncestor(3)).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].GetAncestor(3) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(3 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].GetAncestor(3) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(3 AS smallint)"),
+ Condense(_db.Sql));
- Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
- }
+ Assert.All(results, h => Assert.Equal(HierarchyId.GetRoot(), h));
+ }
- [Fact]
- public void GetAncestor_of_root_returns_null()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 0
- select p.Id.GetAncestor(1));
+ [ConditionalFact]
+ public void GetAncestor_of_root_returns_null()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 0
+ select p.Id.GetAncestor(1)).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].GetAncestor(1) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].GetAncestor(1) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
+ Condense(_db.Sql));
- Assert.Equal(new HierarchyId[] { null }, results);
- }
+ Assert.Equal(new HierarchyId[] { null }, results);
+ }
- [Fact]
- public void GetDescendent_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 0
- select p.Id.GetDescendant(null, null));
+ [ConditionalFact]
+ public void GetDescendent_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 0
+ select p.Id.GetDescendant(null, null)).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Id].GetDescendant(NULL, NULL) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].GetDescendant(NULL, NULL) FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(0 AS smallint)"),
+ Condense(_db.Sql));
- Assert.Equal(new[] { HierarchyId.Parse("/1/") }, results);
- }
+ Assert.Equal(new[] { HierarchyId.Parse("/1/") }, results);
+ }
- [Fact]
- public void HierarchyId_can_be_sent_as_parameter()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id == HierarchyId.Parse("/1/")
- select p.Name);
+ [ConditionalFact]
+ public void HierarchyId_can_be_sent_as_parameter()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id == HierarchyId.Parse("/1/")
+ select p.Name).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id] = '/1/'"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id] = '/1/'"),
+ Condense(_db.Sql));
- Assert.Equal(new[] { "Isaac" }, results);
- }
+ Assert.Equal(new[] { "Isaac" }, results);
+ }
- [Fact]
- public void Converted_HierarchyId_can_be_sent_as_parameter()
- {
- var results = Enumerable.ToList(
- from p in _db.ConvertedPatriarchy
- where p.HierarchyId == HierarchyId.Parse("/1/").ToString()
- select p.Name);
+ [ConditionalFact]
+ public void Converted_HierarchyId_can_be_sent_as_parameter()
+ {
+ var results = (from p in _db.ConvertedPatriarchy
+ where p.HierarchyId == HierarchyId.Parse("/1/").ToString()
+ select p.Name).ToList();
- Assert.Equal(
- condense(@"SELECT [c].[Name] FROM [ConvertedPatriarchy] AS [c] WHERE [c].[HierarchyId] = '/1/'"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [c].[Name] FROM [ConvertedPatriarchy] AS [c] WHERE [c].[HierarchyId] = '/1/'"),
+ Condense(_db.Sql));
- Assert.Equal(new[] { "Isaac" }, results);
- }
+ Assert.Equal(new[] { "Isaac" }, results);
+ }
- [Fact]
- public void Can_insert_HierarchyId()
+ [ConditionalFact]
+ public void Can_insert_HierarchyId()
+ {
+ using (_db.Database.BeginTransaction())
{
- using (_db.Database.BeginTransaction())
+ var entities = new List
{
- var entities = new List
- {
- new() { Id = HierarchyId.Parse("/2/1/"), Name = "Thrór" },
- new() { Id = HierarchyId.Parse("/2/2/"), Name = "Thráin II" },
- new() { Id = HierarchyId.Parse("/3/"), Name = "Thorin Oakenshield" }
- };
+ new() { Id = HierarchyId.Parse("/2/1/"), Name = "Thrór" },
+ new() { Id = HierarchyId.Parse("/2/2/"), Name = "Thráin II" },
+ new() { Id = HierarchyId.Parse("/3/"), Name = "Thorin Oakenshield" }
+ };
- _db.AddRange(entities);
- _db.SaveChanges();
- _db.ChangeTracker.Clear();
+ _db.AddRange(entities);
+ _db.SaveChanges();
+ _db.ChangeTracker.Clear();
- var queried = _db.Patriarchy.Where(e => e.Name.StartsWith("Th")).OrderBy(e => e.Id).ToList();
+ var queried = _db.Patriarchy.Where(e => e.Name.StartsWith("Th")).OrderBy(e => e.Id).ToList();
- Assert.Equal(3, queried.Count);
+ Assert.Equal(3, queried.Count);
- Assert.Equal(HierarchyId.Parse("/2/1/"), queried[0].Id);
- Assert.Equal("Thrór", queried[0].Name);
+ Assert.Equal(HierarchyId.Parse("/2/1/"), queried[0].Id);
+ Assert.Equal("Thrór", queried[0].Name);
- Assert.Equal(HierarchyId.Parse("/2/2/"), queried[1].Id);
- Assert.Equal("Thráin II", queried[1].Name);
+ Assert.Equal(HierarchyId.Parse("/2/2/"), queried[1].Id);
+ Assert.Equal("Thráin II", queried[1].Name);
- Assert.Equal(HierarchyId.Parse("/3/"), queried[2].Id);
- Assert.Equal("Thorin Oakenshield", queried[2].Name);
- }
+ Assert.Equal(HierarchyId.Parse("/3/"), queried[2].Id);
+ Assert.Equal("Thorin Oakenshield", queried[2].Name);
}
+ }
- [Fact]
- public void Can_insert_and_update_converted_HierarchyId()
+ [ConditionalFact]
+ public void Can_insert_and_update_converted_HierarchyId()
+ {
+ using (_db.Database.BeginTransaction())
{
- using (_db.Database.BeginTransaction())
+ var entities = new List
{
- var entities = new List
- {
- new() { HierarchyId = HierarchyId.Parse("/2/1/").ToString(), Name = "Thrór" },
- new() { HierarchyId = HierarchyId.Parse("/2/2/").ToString(), Name = "Thráin II" },
- new() { HierarchyId = HierarchyId.Parse("/3/").ToString(), Name = "Thorin Oakenshield" }
- };
+ new() { HierarchyId = HierarchyId.Parse("/2/1/").ToString(), Name = "Thrór" },
+ new() { HierarchyId = HierarchyId.Parse("/2/2/").ToString(), Name = "Thráin II" },
+ new() { HierarchyId = HierarchyId.Parse("/3/").ToString(), Name = "Thorin Oakenshield" }
+ };
- _db.AddRange(entities);
- _db.SaveChanges();
- _db.ChangeTracker.Clear();
+ _db.AddRange(entities);
+ _db.SaveChanges();
+ _db.ChangeTracker.Clear();
- var queried = _db.ConvertedPatriarchy.Where(e => e.Name.StartsWith("Th")).OrderBy(e => e.Id).ToList();
+ var queried = _db.ConvertedPatriarchy.Where(e => e.Name.StartsWith("Th")).OrderBy(e => e.Id).ToList();
- Assert.Equal(3, queried.Count);
+ Assert.Equal(3, queried.Count);
- Assert.Equal(HierarchyId.Parse("/2/1/").ToString(), queried[0].HierarchyId);
- Assert.Equal("Thrór", queried[0].Name);
+ Assert.Equal(HierarchyId.Parse("/2/1/").ToString(), queried[0].HierarchyId);
+ Assert.Equal("Thrór", queried[0].Name);
- Assert.Equal(HierarchyId.Parse("/2/2/").ToString(), queried[1].HierarchyId);
- Assert.Equal("Thráin II", queried[1].Name);
+ Assert.Equal(HierarchyId.Parse("/2/2/").ToString(), queried[1].HierarchyId);
+ Assert.Equal("Thráin II", queried[1].Name);
- Assert.Equal(HierarchyId.Parse("/3/").ToString(), queried[2].HierarchyId);
- Assert.Equal("Thorin Oakenshield", queried[2].Name);
+ Assert.Equal(HierarchyId.Parse("/3/").ToString(), queried[2].HierarchyId);
+ Assert.Equal("Thorin Oakenshield", queried[2].Name);
- queried[2].HierarchyId = "/3/1/";
+ queried[2].HierarchyId = "/3/1/";
- _db.SaveChanges();
- _db.ChangeTracker.Clear();
+ _db.SaveChanges();
+ _db.ChangeTracker.Clear();
- queried = _db.ConvertedPatriarchy.Where(e => e.Name.StartsWith("Th")).OrderBy(e => e.Id).ToList();
+ queried = _db.ConvertedPatriarchy.Where(e => e.Name.StartsWith("Th")).OrderBy(e => e.Id).ToList();
- Assert.Equal(3, queried.Count);
+ Assert.Equal(3, queried.Count);
- Assert.Equal(HierarchyId.Parse("/2/1/").ToString(), queried[0].HierarchyId);
- Assert.Equal("Thrór", queried[0].Name);
+ Assert.Equal(HierarchyId.Parse("/2/1/").ToString(), queried[0].HierarchyId);
+ Assert.Equal("Thrór", queried[0].Name);
- Assert.Equal(HierarchyId.Parse("/2/2/").ToString(), queried[1].HierarchyId);
- Assert.Equal("Thráin II", queried[1].Name);
+ Assert.Equal(HierarchyId.Parse("/2/2/").ToString(), queried[1].HierarchyId);
+ Assert.Equal("Thráin II", queried[1].Name);
- Assert.Equal(HierarchyId.Parse("/3/1/").ToString(), queried[2].HierarchyId);
- Assert.Equal("Thorin Oakenshield", queried[2].Name);
- }
+ Assert.Equal(HierarchyId.Parse("/3/1/").ToString(), queried[2].HierarchyId);
+ Assert.Equal("Thorin Oakenshield", queried[2].Name);
}
+ }
- [Fact]
- public void HierarchyId_get_ancestor_of_level_is_root()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetAncestor(p.Id.GetLevel()) == HierarchyId.GetRoot() // HierarchyId.Parse("/1/") // HierarchyId.Parse(p.Id.ToString()).GetAncestor(HierarchyId.Parse(p.Id.ToString()).GetLevel())
- select p.Name);
+ [ConditionalFact]
+ public void HierarchyId_get_ancestor_of_level_is_root()
+ {
+ var results = (from p in _db.Patriarchy
+ where
+ p.Id.GetAncestor(p.Id.GetLevel())
+ == HierarchyId
+ .GetRoot() // HierarchyId.Parse("/1/") // HierarchyId.Parse(p.Id.ToString()).GetAncestor(HierarchyId.Parse(p.Id.ToString()).GetLevel())
+ select p.Name).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id].GetAncestor(CAST([p].[Id].GetLevel() AS int)) = '/'"),
- condense(_db.Sql));
+ Assert.Equal(
+ Condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id].GetAncestor(CAST([p].[Id].GetLevel() AS int)) = '/'"),
+ Condense(_db.Sql));
- var all = Enumerable.ToList(
- from p in _db.Patriarchy
- select p.Name);
+ var all = (from p in _db.Patriarchy
+ select p.Name).ToList();
- Assert.Equal(all, results);
- }
+ Assert.Equal(all, results);
+ }
- [Fact]
- public void HierarchyId_can_call_method_on_parameter()
- {
- var isaac = HierarchyId.Parse("/1/");
+ [ConditionalFact]
+ public void HierarchyId_can_call_method_on_parameter()
+ {
+ var isaac = HierarchyId.Parse("/1/");
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where isaac.IsDescendantOf(p.Id)
- select p.Name);
+ var results = (from p in _db.Patriarchy
+ where isaac.IsDescendantOf(p.Id)
+ select p.Name).ToList();
- Assert.Equal(
- condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE @__isaac_0.IsDescendantOf([p].[Id]) = CAST(1 AS bit)"),
- condense(_db.Sql));
+ Assert.Equal(
+ """
+ @__isaac_0='?' (Size = 1) (DbType = Object)
- Assert.Equal(new[] { "Abraham", "Isaac" }, results);
- }
+ SELECT [p].[Name]
+ FROM [Patriarchy] AS [p]
+ WHERE @__isaac_0.IsDescendantOf([p].[Id]) = CAST(1 AS bit)
+ """,
+ _db.Sql,
+ ignoreLineEndingDifferences: true);
- [Fact]
- public void ToString_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id.GetLevel() == 1
- select p.Id.ToString());
+ Assert.Equal(new[] { "Abraham", "Isaac" }, results);
+ }
- Assert.Equal(
- condense(@"SELECT [p].[Id].ToString() FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(1 AS smallint)"),
- condense(_db.Sql));
+ [ConditionalFact]
+ public void ToString_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id.GetLevel() == 1
+ select p.Id.ToString()).ToList();
- Assert.Equal(new[] { "/1/" }, results);
- }
+ Assert.Equal(
+ Condense(@"SELECT [p].[Id].ToString() FROM [Patriarchy] AS [p] WHERE [p].[Id].GetLevel() = CAST(1 AS smallint)"),
+ Condense(_db.Sql));
- [Fact]
- public void ToString_can_translate_redux()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where EF.Functions.Like(p.Id.ToString(), "%/1/")
- select p.Name);
+ Assert.Equal(new[] { "/1/" }, results);
+ }
- Assert.Equal(
- condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id].ToString() LIKE N'%/1/'"),
- condense(_db.Sql));
+ [ConditionalFact]
+ public void ToString_can_translate_redux()
+ {
+ var results = (from p in _db.Patriarchy
+ where EF.Functions.Like(p.Id.ToString(), "%/1/")
+ select p.Name).ToList();
- Assert.Equal(new[] { "Isaac", "Jacob", "Reuben" }, results);
- }
+ Assert.Equal(
+ Condense(@"SELECT [p].[Name] FROM [Patriarchy] AS [p] WHERE [p].[Id].ToString() LIKE N'%/1/'"),
+ Condense(_db.Sql));
- [Fact]
- public void Parse_can_translate()
- {
- var results = Enumerable.ToList(
- from p in _db.Patriarchy
- where p.Id == HierarchyId.GetRoot()
- select HierarchyId.Parse(p.Id.ToString()));
+ Assert.Equal(new[] { "Isaac", "Jacob", "Reuben" }, results);
+ }
- Assert.Equal(
- condense(@"SELECT hierarchyid::Parse([p].[Id].ToString()) FROM [Patriarchy] AS [p] WHERE [p].[Id] = '/'"),
- condense(_db.Sql));
+ [ConditionalFact]
+ public void Parse_can_translate()
+ {
+ var results = (from p in _db.Patriarchy
+ where p.Id == HierarchyId.GetRoot()
+ select HierarchyId.Parse(p.Id.ToString())).ToList();
- Assert.Equal(new[] { HierarchyId.Parse("/") }, results);
- }
+ Assert.Equal(
+ Condense(@"SELECT hierarchyid::Parse([p].[Id].ToString()) FROM [Patriarchy] AS [p] WHERE [p].[Id] = '/'"),
+ Condense(_db.Sql));
- public void Dispose()
- {
- _db.Dispose();
- }
+ Assert.Equal(new[] { HierarchyId.Parse("/") }, results);
+ }
- // replace whitespace with a single space
- private static string condense(string str)
- {
- var split = str.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
- return string.Join(" ", split);
- }
+ public void Dispose()
+ {
+ _db.Dispose();
+ }
+
+ // replace whitespace with a single space
+ private static string Condense(string str)
+ {
+ var split = str.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
+ return string.Join(" ", split);
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/RelationalScaffoldingModelFactoryTest.cs b/test/EFCore.SqlServer.HierarchyId.Tests/RelationalScaffoldingModelFactoryTest.cs
index 987d6865a1e..543499cbf67 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/RelationalScaffoldingModelFactoryTest.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/RelationalScaffoldingModelFactoryTest.cs
@@ -1,3 +1,6 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding;
@@ -8,8 +11,6 @@
namespace Microsoft.EntityFrameworkCore.SqlServer;
-#pragma warning disable EF1001
-
public class RelationalScaffoldingModelFactoryTest
{
private readonly IScaffoldingModelFactory _factory;
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Logging/TestLogger.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Logging/TestLogger.cs
deleted file mode 100644
index 3906af28440..00000000000
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Logging/TestLogger.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-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 state)
- => null;
-
- public bool IsEnabled(LogLevel logLevel)
- => true;
-
- public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
- {
- if (eventId != RelationalEventId.CommandExecuting)
- return;
-
- var structure = (IReadOnlyList>)state;
- var commandText = (string)structure.FirstOrDefault(i => i.Key == "commandText").Value;
-
- Sql = commandText;
- }
- }
-}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Logging/TestLoggerFactory.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Logging/TestLoggerFactory.cs
deleted file mode 100644
index 0d2f8efd9bb..00000000000
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Logging/TestLoggerFactory.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using Microsoft.Extensions.Logging;
-
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Logging
-{
- class TestLoggerFactory : ILoggerFactory
- {
- public TestLogger Logger { get; }
- = new TestLogger();
-
- public void AddProvider(ILoggerProvider provider)
- => throw new NotImplementedException();
-
- public ILogger CreateLogger(string categoryName)
- => Logger;
-
- public void Dispose()
- {
- }
- }
-}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/AbrahamicContext.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/AbrahamicContext.cs
index 8b3f94d9aa6..5a63392b9c5 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/AbrahamicContext.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/AbrahamicContext.cs
@@ -1,69 +1,154 @@
-using Microsoft.EntityFrameworkCore.SqlServer.Test.Logging;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models
+using Microsoft.EntityFrameworkCore.TestUtilities;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models;
+
+internal class AbrahamicContext : DbContext
{
- class AbrahamicContext : DbContext
- {
- readonly TestLoggerFactory _loggerFactory
- = new TestLoggerFactory();
+ private readonly TestSqlLoggerFactory _loggerFactory = new();
- public DbSet Patriarchy { get; set; }
- public DbSet ConvertedPatriarchy { get; set; }
+ public DbSet Patriarchy { get; set; }
+ public DbSet ConvertedPatriarchy { get; set; }
- public string Sql
- => _loggerFactory.Logger.Sql;
+ public string Sql
+ => _loggerFactory.Sql;
- protected override void OnConfiguring(DbContextOptionsBuilder options)
- => options
- .UseSqlServer(
- @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=HierarchyIdTests",
- x => x.UseHierarchyId())
- .UseLoggerFactory(_loggerFactory);
+ public void ClearSql()
+ => _loggerFactory.Clear();
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity()
- .HasData(
- new Patriarch { Id = HierarchyId.GetRoot(), Name = "Abraham" },
- new Patriarch { Id = HierarchyId.Parse("/1/"), Name = "Isaac" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/"), Name = "Jacob" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/1/"), Name = "Reuben" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/2/"), Name = "Simeon" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/3/"), Name = "Levi" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/4/"), Name = "Judah" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/5/"), Name = "Issachar" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/6/"), Name = "Zebulun" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/7/"), Name = "Dan" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/8/"), Name = "Naphtali" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/9/"), Name = "Gad" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/10/"), Name = "Asher" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/11.1/"), Name = "Ephraim" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/11.2/"), Name = "Manasseh" },
- new Patriarch { Id = HierarchyId.Parse("/1/1/12/"), Name = "Benjamin" });
+ protected override void OnConfiguring(DbContextOptionsBuilder options)
+ => options
+ .UseSqlServer(
+ SqlServerTestStore.CreateConnectionString("HierarchyIdTests"),
+ x => x.UseHierarchyId())
+ .UseLoggerFactory(_loggerFactory);
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasData(
+ new Patriarch { Id = HierarchyId.GetRoot(), Name = "Abraham" },
+ new Patriarch { Id = HierarchyId.Parse("/1/"), Name = "Isaac" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/"), Name = "Jacob" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/1/"), Name = "Reuben" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/2/"), Name = "Simeon" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/3/"), Name = "Levi" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/4/"), Name = "Judah" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/5/"), Name = "Issachar" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/6/"), Name = "Zebulun" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/7/"), Name = "Dan" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/8/"), Name = "Naphtali" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/9/"), Name = "Gad" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/10/"), Name = "Asher" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/11.1/"), Name = "Ephraim" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/11.2/"), Name = "Manasseh" },
+ new Patriarch { Id = HierarchyId.Parse("/1/1/12/"), Name = "Benjamin" });
- modelBuilder.Entity(b =>
+ modelBuilder.Entity(
+ b =>
{
b.Property(e => e.HierarchyId)
.HasConversion(v => HierarchyId.Parse(v), v => v.ToString());
b.HasData(
- new ConvertedPatriarch { Id = 1, HierarchyId = HierarchyId.GetRoot().ToString(), Name = "Abraham" },
- new ConvertedPatriarch { Id = 2, HierarchyId = HierarchyId.Parse("/1/").ToString(), Name = "Isaac" },
- new ConvertedPatriarch { Id = 3, HierarchyId = HierarchyId.Parse("/1/1/").ToString(), Name = "Jacob" },
- new ConvertedPatriarch { Id = 4, HierarchyId = HierarchyId.Parse("/1/1/1/").ToString(), Name = "Reuben" },
- new ConvertedPatriarch { Id = 5, HierarchyId = HierarchyId.Parse("/1/1/2/").ToString(), Name = "Simeon" },
- new ConvertedPatriarch { Id = 6, HierarchyId = HierarchyId.Parse("/1/1/3/").ToString(), Name = "Levi" },
- new ConvertedPatriarch { Id = 7, HierarchyId = HierarchyId.Parse("/1/1/4/").ToString(), Name = "Judah" },
- new ConvertedPatriarch { Id = 8, HierarchyId = HierarchyId.Parse("/1/1/5/").ToString(), Name = "Issachar" },
- new ConvertedPatriarch { Id = 9, HierarchyId = HierarchyId.Parse("/1/1/6/").ToString(), Name = "Zebulun" },
- new ConvertedPatriarch { Id = 10, HierarchyId = HierarchyId.Parse("/1/1/7/").ToString(), Name = "Dan" },
- new ConvertedPatriarch { Id = 11, HierarchyId = HierarchyId.Parse("/1/1/8/").ToString(), Name = "Naphtali" },
- new ConvertedPatriarch { Id = 12, HierarchyId = HierarchyId.Parse("/1/1/9/").ToString(), Name = "Gad" },
- new ConvertedPatriarch { Id = 13, HierarchyId = HierarchyId.Parse("/1/1/10/").ToString(), Name = "Asher" },
- new ConvertedPatriarch { Id = 14, HierarchyId = HierarchyId.Parse("/1/1/11.1/").ToString(), Name = "Ephraim" },
- new ConvertedPatriarch { Id = 15, HierarchyId = HierarchyId.Parse("/1/1/11.2/").ToString(), Name = "Manasseh" },
- new ConvertedPatriarch { Id = 16, HierarchyId = HierarchyId.Parse("/1/1/12/").ToString(), Name = "Benjamin" });
+ new ConvertedPatriarch
+ {
+ Id = 1,
+ HierarchyId = HierarchyId.GetRoot().ToString(),
+ Name = "Abraham"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 2,
+ HierarchyId = HierarchyId.Parse("/1/").ToString(),
+ Name = "Isaac"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 3,
+ HierarchyId = HierarchyId.Parse("/1/1/").ToString(),
+ Name = "Jacob"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 4,
+ HierarchyId = HierarchyId.Parse("/1/1/1/").ToString(),
+ Name = "Reuben"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 5,
+ HierarchyId = HierarchyId.Parse("/1/1/2/").ToString(),
+ Name = "Simeon"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 6,
+ HierarchyId = HierarchyId.Parse("/1/1/3/").ToString(),
+ Name = "Levi"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 7,
+ HierarchyId = HierarchyId.Parse("/1/1/4/").ToString(),
+ Name = "Judah"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 8,
+ HierarchyId = HierarchyId.Parse("/1/1/5/").ToString(),
+ Name = "Issachar"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 9,
+ HierarchyId = HierarchyId.Parse("/1/1/6/").ToString(),
+ Name = "Zebulun"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 10,
+ HierarchyId = HierarchyId.Parse("/1/1/7/").ToString(),
+ Name = "Dan"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 11,
+ HierarchyId = HierarchyId.Parse("/1/1/8/").ToString(),
+ Name = "Naphtali"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 12,
+ HierarchyId = HierarchyId.Parse("/1/1/9/").ToString(),
+ Name = "Gad"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 13,
+ HierarchyId = HierarchyId.Parse("/1/1/10/").ToString(),
+ Name = "Asher"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 14,
+ HierarchyId = HierarchyId.Parse("/1/1/11.1/").ToString(),
+ Name = "Ephraim"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 15,
+ HierarchyId = HierarchyId.Parse("/1/1/11.2/").ToString(),
+ Name = "Manasseh"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 16,
+ HierarchyId = HierarchyId.Parse("/1/1/12/").ToString(),
+ Name = "Benjamin"
+ });
});
- }
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/ConvertedPatriarch.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/ConvertedPatriarch.cs
index a5ca81ce2b1..a7c449612df 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/ConvertedPatriarch.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/ConvertedPatriarch.cs
@@ -1,9 +1,11 @@
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models;
+
+internal class ConvertedPatriarch
{
- class ConvertedPatriarch
- {
- public int Id { get; set; }
- public string HierarchyId { get; set; }
- public string Name { get; set; }
- }
-}
\ No newline at end of file
+ public int Id { get; set; }
+ public string HierarchyId { get; set; }
+ public string Name { get; set; }
+}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/AnonymousArraySeedContext.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/AnonymousArraySeedContext.cs
index b448b23ca89..2ea2780dfff 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/AnonymousArraySeedContext.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/AnonymousArraySeedContext.cs
@@ -1,33 +1,52 @@
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models.Migrations
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models.Migrations;
+
+internal sealed class AnonymousArraySeedContext : MigrationContext
{
- internal sealed class AnonymousArraySeedContext : MigrationContext
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- RemoveVariableModelAnnotations(modelBuilder);
+ RemoveVariableModelAnnotations(modelBuilder);
- modelBuilder.Entity().HasData(
- new { Id = HierarchyId.GetRoot(), Name = "Eddard Stark" },
- new { Id = HierarchyId.Parse("/1/"), Name = "Robb Stark" },
- new { Id = HierarchyId.Parse("/2/"), Name = "Jon Snow" });
+ modelBuilder.Entity().HasData(
+ new { Id = HierarchyId.GetRoot(), Name = "Eddard Stark" },
+ new { Id = HierarchyId.Parse("/1/"), Name = "Robb Stark" },
+ new { Id = HierarchyId.Parse("/2/"), Name = "Jon Snow" });
- modelBuilder.Entity(b =>
+ modelBuilder.Entity(
+ b =>
{
b.Property(e => e.HierarchyId)
.HasConversion(v => HierarchyId.Parse(v), v => v.ToString());
b.HasData(
- new ConvertedPatriarch { Id = 1, HierarchyId = HierarchyId.GetRoot().ToString(), Name = "Eddard Stark" },
- new ConvertedPatriarch { Id = 2, HierarchyId = HierarchyId.Parse("/1/").ToString(), Name = "Robb Stark" },
- new ConvertedPatriarch { Id = 3, HierarchyId = HierarchyId.Parse("/2/").ToString(), Name = "Jon Snow" });
+ new ConvertedPatriarch
+ {
+ Id = 1,
+ HierarchyId = HierarchyId.GetRoot().ToString(),
+ Name = "Eddard Stark"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 2,
+ HierarchyId = HierarchyId.Parse("/1/").ToString(),
+ Name = "Robb Stark"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 3,
+ HierarchyId = HierarchyId.Parse("/2/").ToString(),
+ Name = "Jon Snow"
+ });
});
- }
+ }
- public override string GetExpectedMigrationCode(string migrationName, string rootNamespace)
- {
- return $@"using Microsoft.EntityFrameworkCore;
+ public override string GetExpectedMigrationCode(string migrationName, string rootNamespace)
+ {
+ return $@"using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
@@ -100,11 +119,11 @@ protected override void Down(MigrationBuilder migrationBuilder)
}}
}}
";
- }
+ }
- public override string GetExpectedSnapshotCode(string rootNamespace)
- {
- return $@"//
+ public override string GetExpectedSnapshotCode(string rootNamespace)
+ {
+ return $@"//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using {ThisType.Namespace};
@@ -192,6 +211,5 @@ protected override void BuildModel(ModelBuilder modelBuilder)
}}
}}
";
- }
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/MigrationContext.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/MigrationContext.cs
index 940d136502f..442b86efedb 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/MigrationContext.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/MigrationContext.cs
@@ -1,48 +1,49 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal;
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models.Migrations
+namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models.Migrations;
+
+internal abstract class MigrationContext : DbContext
+ where TEntity1 : class
+ where TEntity2 : class
{
- internal abstract class MigrationContext : DbContext
- where TEntity1 : class
- where TEntity2 : class
+ protected Type ModelType1 { get; } = typeof(TEntity1);
+ protected Type ModelType2 { get; } = typeof(TEntity2);
+
+ private Type _thisType;
+
+ protected Type ThisType
+ => _thisType ??= GetType();
+
+ public DbSet TestModels { get; set; }
+ public DbSet ConvertedTestModels { get; set; }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder options)
+ => options
+ .UseSqlServer(
+ @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=HierarchyIdMigrationTests",
+ x => x.UseHierarchyId());
+
+ ///
+ /// Removes annotations from the model that can
+ /// change between versions of ef.
+ /// This should be called during OnModelCreating
+ ///
+ protected void RemoveVariableModelAnnotations(ModelBuilder modelBuilder)
{
- protected Type ModelType1 { get; } = typeof(TEntity1);
- protected Type ModelType2 { get; } = typeof(TEntity2);
-
- private Type _thisType;
- protected Type ThisType => _thisType ??= GetType();
-
- public DbSet TestModels { get; set; }
- public DbSet ConvertedTestModels { get; set; }
-
- protected override void OnConfiguring(DbContextOptionsBuilder options)
- => options
- .UseSqlServer(
- @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=HierarchyIdMigrationTests",
- x => x.UseHierarchyId());
-
- ///
- /// Removes annotations from the model that can
- /// change between versions of ef.
- /// This should be called during OnModelCreating
- ///
- ///
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "Uses internal efcore apis.")]
- protected void RemoveVariableModelAnnotations(ModelBuilder modelBuilder)
- {
- var model = modelBuilder.Model;
-
- //the values of these could change between versions
- //so get rid of them for the tests
- model.RemoveAnnotation(CoreAnnotationNames.ProductVersion);
- model.RemoveAnnotation(RelationalAnnotationNames.MaxIdentifierLength);
- model.RemoveAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy);
- }
-
- public abstract string GetExpectedMigrationCode(string migrationName, string rootNamespace);
- public abstract string GetExpectedSnapshotCode(string rootNamespace);
+ var model = modelBuilder.Model;
+
+ //the values of these could change between versions
+ //so get rid of them for the tests
+ model.RemoveAnnotation(CoreAnnotationNames.ProductVersion);
+ model.RemoveAnnotation(RelationalAnnotationNames.MaxIdentifierLength);
+ model.RemoveAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy);
}
+
+ public abstract string GetExpectedMigrationCode(string migrationName, string rootNamespace);
+ public abstract string GetExpectedSnapshotCode(string rootNamespace);
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/TypedArraySeedContext.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/TypedArraySeedContext.cs
index 6b04e71c517..d9e28718acc 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/TypedArraySeedContext.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Migrations/TypedArraySeedContext.cs
@@ -1,33 +1,52 @@
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models.Migrations
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models.Migrations;
+
+internal sealed class TypedArraySeedContext : MigrationContext
{
- internal sealed class TypedArraySeedContext : MigrationContext
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- RemoveVariableModelAnnotations(modelBuilder);
+ RemoveVariableModelAnnotations(modelBuilder);
- modelBuilder.Entity().HasData(
- new Patriarch { Id = HierarchyId.GetRoot(), Name = "Eddard Stark" },
- new Patriarch { Id = HierarchyId.Parse("/1/"), Name = "Robb Stark" },
- new Patriarch { Id = HierarchyId.Parse("/2/"), Name = "Jon Snow" });
+ modelBuilder.Entity().HasData(
+ new Patriarch { Id = HierarchyId.GetRoot(), Name = "Eddard Stark" },
+ new Patriarch { Id = HierarchyId.Parse("/1/"), Name = "Robb Stark" },
+ new Patriarch { Id = HierarchyId.Parse("/2/"), Name = "Jon Snow" });
- modelBuilder.Entity(b =>
+ modelBuilder.Entity(
+ b =>
{
b.Property(e => e.HierarchyId)
.HasConversion(v => HierarchyId.Parse(v), v => v.ToString());
b.HasData(
- new ConvertedPatriarch { Id = 1, HierarchyId = HierarchyId.GetRoot().ToString(), Name = "Eddard Stark" },
- new ConvertedPatriarch { Id = 2, HierarchyId = HierarchyId.Parse("/1/").ToString(), Name = "Robb Stark" },
- new ConvertedPatriarch { Id = 3, HierarchyId = HierarchyId.Parse("/2/").ToString(), Name = "Jon Snow" });
+ new ConvertedPatriarch
+ {
+ Id = 1,
+ HierarchyId = HierarchyId.GetRoot().ToString(),
+ Name = "Eddard Stark"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 2,
+ HierarchyId = HierarchyId.Parse("/1/").ToString(),
+ Name = "Robb Stark"
+ },
+ new ConvertedPatriarch
+ {
+ Id = 3,
+ HierarchyId = HierarchyId.Parse("/2/").ToString(),
+ Name = "Jon Snow"
+ });
});
- }
+ }
- public override string GetExpectedMigrationCode(string migrationName, string rootNamespace)
- {
- return $@"using Microsoft.EntityFrameworkCore;
+ public override string GetExpectedMigrationCode(string migrationName, string rootNamespace)
+ {
+ return $@"using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
@@ -100,11 +119,11 @@ protected override void Down(MigrationBuilder migrationBuilder)
}}
}}
";
- }
+ }
- public override string GetExpectedSnapshotCode(string rootNamespace)
- {
- return $@"//
+ public override string GetExpectedSnapshotCode(string rootNamespace)
+ {
+ return $@"//
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using {ThisType.Namespace};
@@ -192,6 +211,5 @@ protected override void BuildModel(ModelBuilder modelBuilder)
}}
}}
";
- }
}
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Patriarch.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Patriarch.cs
index 2e7712a4c61..080d4c3e44f 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Patriarch.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Models/Patriarch.cs
@@ -1,8 +1,10 @@
-namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Test.Models;
+
+internal class Patriarch
{
- class Patriarch
- {
- public HierarchyId Id { get; set; }
- public string Name { get; set; }
- }
+ public HierarchyId Id { get; set; }
+ public string Name { get; set; }
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/FakeScaffoldingModelFactory.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/FakeScaffoldingModelFactory.cs
index e73193686fe..55c118a8cca 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/FakeScaffoldingModelFactory.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/FakeScaffoldingModelFactory.cs
@@ -1,6 +1,6 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -11,8 +11,6 @@
namespace Microsoft.EntityFrameworkCore.TestUtilities;
-#pragma warning disable EF1001
-
public class FakeScaffoldingModelFactory : RelationalScaffoldingModelFactory
{
public FakeScaffoldingModelFactory(
@@ -22,7 +20,8 @@ public FakeScaffoldingModelFactory(
ICSharpUtilities cSharpUtilities,
IScaffoldingTypeMapper scaffoldingTypeMapper,
IModelRuntimeInitializer modelRuntimeInitializer)
- : base(reporter, candidateNamingService, pluralizer, cSharpUtilities, scaffoldingTypeMapper,
+ : base(
+ reporter, candidateNamingService, pluralizer, cSharpUtilities, scaffoldingTypeMapper,
modelRuntimeInitializer)
{
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/SqlServerTestHelpers.cs b/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/SqlServerTestHelpers.cs
deleted file mode 100644
index f74e93f88ad..00000000000
--- a/test/EFCore.SqlServer.HierarchyId.Tests/Test/Utilities/SqlServerTestHelpers.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Microsoft.Data.SqlClient;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.SqlServer.Diagnostics.Internal;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.TestUtilities;
-
-#pragma warning disable EF1001
-
-public class SqlServerTestHelpers : TestHelpers
-{
- private SqlServerTestHelpers()
- {
- }
-
- public static SqlServerTestHelpers Instance { get; } = new();
-
- public override IServiceCollection AddProviderServices(IServiceCollection services)
- => services.AddEntityFrameworkSqlServer();
-
- public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder)
- => optionsBuilder.UseSqlServer(new SqlConnection("Database=DummyDatabase"));
-
- public override LoggingDefinitions LoggingDefinitions { get; } = new SqlServerLoggingDefinitions();
-}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs
index 79bfa933a51..c756affa5c8 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs
@@ -1,56 +1,57 @@
-using System;
-using Microsoft.EntityFrameworkCore.SqlServer.Storage;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Xunit;
-namespace Microsoft.EntityFrameworkCore.SqlServer
+namespace Microsoft.EntityFrameworkCore.SqlServer;
+
+public class TypeMappingTests
{
- public class TypeMappingTests
+ [ConditionalFact]
+ public void Maps_int_column()
+ {
+ var mapping = CreateMapper().FindMapping(
+ new RelationalTypeMappingInfo(
+ storeTypeName: "int",
+ storeTypeNameBase: "int",
+ unicode: null,
+ size: null,
+ precision: null,
+ scale: null));
+
+ Assert.Null(mapping);
+ }
+
+ [ConditionalFact]
+ public void Maps_hierarchyid_column()
+ {
+ var mapping = CreateMapper().FindMapping(
+ new RelationalTypeMappingInfo(
+ storeTypeName: SqlServerHierarchyIdTypeMappingSourcePlugin.SqlServerTypeName,
+ storeTypeNameBase: SqlServerHierarchyIdTypeMappingSourcePlugin.SqlServerTypeName,
+ unicode: null,
+ size: null,
+ precision: null,
+ scale: null));
+
+ AssertMapping(mapping);
+ }
+
+ private static void AssertMapping(
+ RelationalTypeMapping mapping)
{
- [Fact]
- public void Maps_int_column()
- {
- var mapping = CreateMapper().FindMapping(
- new RelationalTypeMappingInfo(
- storeTypeName: "int",
- storeTypeNameBase: "int",
- unicode: null,
- size: null,
- precision: null,
- scale: null));
-
- Assert.Null(mapping);
- }
-
- [Fact]
- public void Maps_hierarchyid_column()
- {
- var mapping = CreateMapper().FindMapping(
- new RelationalTypeMappingInfo(
- storeTypeName: SqlServerHierarchyIdTypeMappingSourcePlugin.SqlServerTypeName,
- storeTypeNameBase: SqlServerHierarchyIdTypeMappingSourcePlugin.SqlServerTypeName,
- unicode: null,
- size: null,
- precision: null,
- scale: null));
-
- AssertMapping(mapping);
- }
-
- private static void AssertMapping(
- RelationalTypeMapping mapping)
- {
- AssertMapping(typeof(T), mapping);
- }
-
- private static void AssertMapping(
- Type type,
- RelationalTypeMapping mapping)
- {
- Assert.Same(type, mapping.ClrType);
- }
-
- private static IRelationalTypeMappingSourcePlugin CreateMapper()
- => new SqlServerHierarchyIdTypeMappingSourcePlugin();
+ AssertMapping(typeof(T), mapping);
}
+
+ private static void AssertMapping(
+ Type type,
+ RelationalTypeMapping mapping)
+ {
+ Assert.Same(type, mapping.ClrType);
+ }
+
+ private static IRelationalTypeMappingSourcePlugin CreateMapper()
+ => new SqlServerHierarchyIdTypeMappingSourcePlugin();
}
diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs
index c6a7c62a304..475aacbbdab 100644
--- a/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs
+++ b/test/EFCore.SqlServer.HierarchyId.Tests/WrapperTests.cs
@@ -1,15 +1,38 @@
-using Xunit;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.EntityFrameworkCore.SqlServer
+using Xunit;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer;
+
+public class WrapperTests
{
- public class WrapperTests
- {
- [Fact]
- public void GetAncestor_returns_null_when_too_high()
- => Assert.Null(HierarchyId.Parse("/1/").GetAncestor(2));
-
- [Fact]
- public void GetReparentedValue_returns_null_when_newRoot_is_null()
- => Assert.Null(HierarchyId.Parse("/1/").GetReparentedValue(HierarchyId.GetRoot(), newRoot: null));
- }
+ [ConditionalTheory]
+ [InlineData(null, 1)]
+ [InlineData("/", 1)]
+ [InlineData("/0.5/", 1)]
+ [InlineData("/1/", 0)]
+ [InlineData("/2/", -1)]
+ [InlineData("/1/1/", -1)]
+ public void CompareTo_works(string value, int expected)
+ => Assert.Equal(expected, HierarchyId.Parse("/1/").CompareTo(HierarchyId.Parse(value)));
+
+ [ConditionalTheory]
+ [InlineData(null, false)]
+ [InlineData("/", false)]
+ [InlineData("/1/", true)]
+ public void Equals_works(string value, bool expected)
+ => Assert.Equal(expected, HierarchyId.Parse("/1/").Equals(HierarchyId.Parse(value)));
+
+ [ConditionalFact]
+ public void GetAncestor_returns_null_when_too_high()
+ => Assert.Null(HierarchyId.Parse("/1/").GetAncestor(2));
+
+ [ConditionalFact]
+ public void GetReparentedValue_returns_null_when_newRoot_is_null()
+ => Assert.Null(HierarchyId.Parse("/1/").GetReparentedValue(HierarchyId.GetRoot(), newRoot: null));
+
+ [ConditionalFact]
+ public void IsDescendantOf_returns_false_when_parent_is_null()
+ => Assert.False(HierarchyId.Parse("/1/").IsDescendantOf(null));
}