dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.72k stars 3.17k forks source link

SQL Server: Support hierarchyid #365

Closed mojtabakaviani closed 1 year ago

mojtabakaviani commented 10 years ago

We are using this issue to cover specifically end-to-end support for hierarchyid. SQL Server UDTs are covered by https://github.com/aspnet/EntityFrameworkCore/issues/12796, and spatial is covered by https://github.com/aspnet/EntityFrameworkCore/issues/1100.

As noted in https://github.com/aspnet/EntityFrameworkCore/issues/365#issuecomment-338293994, we already support mapping a property of this type where the type is available.

Here is what is still missing before we can say we support hierarchyid:

Original issue

sql server special data types add in EF5 and later but hierarchyid and user defined types not implemented yet.please work on hierarchyid and user defined types that use in real website and enterprise.

Note by @rowanmiller: Also relevant - [C# Feature Request] Hierarchy Data Type #16193

darthkurak commented 5 years ago

Hello. Can someone explain me a bit: I'm trying to use SqlHierarchyId in my PoC. First, i used Microsoft.SqlServer.Types nuget package to include it. Everything seemed to work, but when i looked at produced query on SQL Profiler for such example: Where(p => p.HierarchyId.IsDescendantOf(anotherHierarchyId)) that is completle ingored, and i have simple From without Where. But in memory, items are filtered and i got only descendants. I checked the same under dotMorten nuget package. Same behavior. This is known (that hierarchyId functions are only applied in memory, after getting items from database) or I'm doing something wrong?

darthkurak commented 5 years ago

Also, i checked aljones solution and it worked pretty well, beside one issue: You can't call sqlHierarchy functions on variables - exception is thrown. Example: Where(p => variable.IsDescendantOf(p)) (for finding all ancestors) Exception: System.Data.SqlClient.SqlException: 'Cannot call methods on varbinary.'

bricelam commented 5 years ago

@darthkurak You need to specify the function mappings:

modelBuilder
    .HasDbFunction(typeof(SqlHierarchyId).GetMethod(nameof(SqlHierarchyId.IsDescendantOf)))
    .HasTranslation(
        args => SqlFunctionExpression.Create(
            args.First(),
            "IsDescendantOf",
            args.Skip(1),
            typeof(bool),
            null)));
bricelam commented 5 years ago

Wait. @smitpatel Is the first expression the instance? How do you map instance methods?

smitpatel commented 5 years ago

Your code snippet is correct. That is how you map instance methods.

bricelam commented 5 years ago

Cannot call methods on varbinary

@aljones Can you leverage this awesome feature like we did for spatial?

darthkurak commented 5 years ago

@bricelam Actually this is something that i tried to do on my own, (on aljones code) but then i hit exception: "Specified type is not registered on the target server. System.Data.SqlTypes.SqlBytes, System.Data.Common, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.'"

Tried to fight with it, but with no success. Have any ideas and suggestions?

aljones commented 5 years ago

I made the suggested changes from @bricelam and other changes to support efcore 3.

@darthkurak I got the same error before updating to efcore 3. I think ef core 3 is using Microsoft.Data.SqlClient which enables the new feature, but I haven't followed that trail to be sure.

bricelam commented 5 years ago

Yes, the feature was added to SqlClient in the 3.0 timeframe

bricelam commented 5 years ago

@aljones Do you ship a NuGet package? If so, we should add it to our list of extensions.

huoyaoyuan commented 5 years ago

My edited version works for simple scenarios, but breaks for second migration(initial was good). I want an officially supported version that doesn't have corner case issues.

darthkurak commented 5 years ago

Thanks for update. Is there possibility to introduce this change in 2.2? We will not update our project to 3.0 yet, and would be great to have working hierarchyId in EF core. Also I agree that definitely we need official version. Is there any chance for that in near future?

aljones commented 5 years ago

I haven't published a nuget package. The project I was going to use this for was delayed. I really only know that a few simple tests pass. If the project gets rescheduled then I can justify spending more time on it, but not until then.

chrisrichards commented 4 years ago

I'm struggling to get migrations to run in EF Core with a HierarchyId column.

I've created a test project that is based on the code from @aljones and @dotMorten but I've replaced the dependency on System.Data.SqlClient with Microsoft.Data.SqlClient.

The QueryTests all pass, but when I attempt to generate migrations from the EFCore.SqlServer.HierarchyId.Test folder it fails as below:

dotnet ef migrations add InitialCreate
Build started...
Build succeeded.
System.InvalidOperationException: The current CSharpHelper cannot scaffold literals of type 'System.Data.SqlTypes.SqlBytes'. Configure your services to use one that can.
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.UnknownLiteral(Object value)
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.Literal(Object[,] values)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationOperationGenerator.Generate(InsertDataOperation operation, IndentedStringBuilder builder)
   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid3[T0,T1,T2](CallSite site, T0 arg0, T1 arg1, T2 arg2)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationOperationGenerator.Generate(String builderName, IReadOnlyList`1 operations, IndentedStringBuilder builder)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateMigration(String migrationNamespace, String migrationName, IReadOnlyList`1 upOperations, IReadOnlyList`1 downOperations)
   at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The current CSharpHelper cannot scaffold literals of type 'System.Data.SqlTypes.SqlBytes'. Configure your services to use one that can.

Is there something that needs adding so that EF Core will generate the migration?

ajcvickers commented 4 years ago

@chrisrichards This looks like it might be a bug--can you create a new issue?

chrisrichards commented 4 years ago

@ajcvickers on the efcore repo?

ajcvickers commented 4 years ago

@chrisrichards Yes, thanks.

chrisrichards commented 4 years ago

@ajcvickers see #19785, thanks

pantonis commented 4 years ago

Is this supported in EF Core 3.1? If yes are there any examples that we can use?

ajcvickers commented 4 years ago

@pantonis Essentially, no. It might work for some scenarios on .NET Framework. It won't work on .NET Core since .NET Core does not have that type. See https://github.com/dotnet/SqlClient/issues/322

bricelam commented 4 years ago

@pantonis I don't know who has the best fork, but some clever folks finished my experiment of using the same approach we used to implement spatial.

bricelam commented 4 years ago

@aljones We should publish a package to NuGet.org

pantonis commented 4 years ago

@bricelam Still dont understand why EF6.3 which was supposed to not get any updates got this important feature and EF Core which is the new era didn't get it. :(

bricelam commented 4 years ago

@pantonis It was contributed to EF6--we just clicked merge. We probably would have done this on EF Core as part of the spatial implementation if Microsoft.SqlServer.Types was supported on .NET Core or even if there was a third-party library like NTS for hierarchyid. Basically, it doesn't seem prudent for our small team to implement, support, and maintain a HierarchyId library for .NET right now, so we're effectively blocked on implementing this.

pantonis commented 4 years ago

@bricelam I totally understand what you are saying. you guys are doing great job but please allow me to ask. Why is Microsoft not investing in EF Core by assigning more resources? I mean it is crazy. Lots of important functionality for an ORM is missing from EF Core (not saying about hierachyId). Is there any specific reason for very very very few resources on ef core development team?

bricelam commented 4 years ago

🤷🏻‍♂️ I just work here. And unfortunately, I'm not sure where the best place to provide that feedback is (which might be part of the problem).

bricelam commented 4 years ago

Some conversation happened here: https://developercommunity.visualstudio.com/idea/793848/increase-budget-for-entity-framework-core-dev-team.html

bricelam commented 4 years ago

But we're pretty off-topic. Let's take this conversation somewhere else. (Sorry everyone watching for hierarchyid)

ErikEJ commented 4 years ago

@ dotmorten - can your library help?

bricelam commented 4 years ago

@ErikEJ That's the approach used by aljones/EFCore.SqlServer.HierarchyId (the experiment I bootstrapped). We use Morten's implementation of Microsoft.SqlServer.Types for the hierarchyid serialization and logic.

ErikEJ commented 4 years ago

Is it published on nuget?

bricelam commented 4 years ago

Filed https://github.com/efcore/EFCore.SqlServer.HierarchyId/issues/1

pantonis commented 4 years ago

@bricelam Thanks a lot. Is this package gonna be part of EF Core 3.1 update?

bricelam commented 4 years ago

To be clear, this is an unsupported third-party package. But yes, it will work with EF Core 3.1

bricelam commented 4 years ago

Published. See https://github.com/efcore/EFCore.SqlServer.HierarchyId

StevenRasmussen commented 4 years ago

@bricelam - Thanks! I just tried installing and the package/s are installed fine but I'm getting some build errors:

image

I think there might be an issue with the way that the 'Abstraction' package is referenced from the main package. It seems like it might be linked as a project reference instead of a package. If I use one of the 'Quick Actions and Refactorings' provided by Visual Studio to try and add a reference it opens up a dialog to add a Project reference instead of NuGet package manager. Anyway, happy to provide more details if necessary. Thanks for your work on this!

StevenRasmussen commented 4 years ago

@bricelam - Sorry... false alarm! I guess I hadn't removed all of my references properly from when I had the project locally. After removing all references to the HierarchyId project/dll and using the NuGet packages fixes everything. Thanks again!

vyrotek commented 3 years ago

Official support is sorely needed!

Ogglas commented 2 years ago

@bricelam @vyrotek Official support would still be appreciated :)

bricelam commented 2 years ago

For sure. We're still waiting on the SQL Server team to release a version of Microsoft.SqlServer.Types that works cross-platform on .NET 6+. Last I heard, they had a plan and were actively working on it.

Indiana Jones Top Men GIF

IT-CASADO commented 2 years ago

Any news about official support?

bricelam commented 2 years ago

This is finally unblocked. Recent previews of the Microsoft.SqlServer.Types package support modern .NET and its hiererchyid type works cross-platform.

mojtabakaviani commented 1 year ago

Thanks everyone! 👍

marchy commented 11 months ago

What is the Sqlite support for this on EF8?

Curious if it's worthwhile updating our code to support the new EF8 hierarchy ID abstraction over simply doing string checks for ancestors on a hierarchical-by-convention ID (ie: Ancestor1/Ancestor2/Node) as we currently do.

ajcvickers commented 11 months ago

@marchy SQLite does not natively support HierarchyId, and we don't plan to add anything to fake it in EF Core.

roji commented 11 months ago

@marchy assuming you're using SQLite as a fake database for testing (with the real database being SQL Server), this is a good example of the limitations of that testing approach. We recommend using e.g. testcontainers.net to effortlessly spin up a real, containerized SQL Server database in your tests, and test against that.