dotnet / SqlClient

Microsoft.Data.SqlClient provides database connectivity to SQL Server for .NET applications.
MIT License
844 stars 280 forks source link

Implement SqlGeography and SqlGeometry for Spatial Types support for .Net Core #30

Closed ststeiger closed 1 year ago

ststeiger commented 6 years ago

Cannot run Microsoft.SqlServer.Types because \Microsoft.SqlServer.Server\IBinarySerialize.cs is missing in System.Data.

Since no source code is available, and contact owners on nuget yields a HTTP-500, I'm opening an issue here. I'd like to compute a polygon union...

#if false
namespace AnySqlWebAdmin
{
    public class GeoPoint
    {
        public decimal Lat;
        public decimal Long;
        public GeoPoint(decimal latitude, decimal longitude)
        {
            this.Lat = latitude;
            this.Long = longitude;
        }
        public GeoPoint() : this(0,0) { }
        public override string ToString()
        {
            return this.Lat.ToString(System.Globalization.CultureInfo.InvariantCulture)
                + " "
                + this.Long.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
    }
    public class GeographicOperations 
    {
        public static string ObjJoin(string separator, params object[] objs)
        {
            string result = null;
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            bool isNotFirst = false;

            for (int i = 0; i < objs.Length; ++i)
            {
                if (objs[i] == null)
                    continue;
                if (separator != null && isNotFirst)
                {
                    sb.Append(separator);
                }
                else
                    isNotFirst = true;

                if(objs[i] != null)
                    sb.Append(objs[i].ToString());
            }

            result = sb.ToString();
            sb.Clear();
            sb = null;

            return result;
        }
        // geography::STPolyFromText('POLYGON((9.3763619 47.4330074,9.3764389 47.4329684,9.3764072 47.4329405,9.3763969 47.4329322,9.3759864 47.4326004,9.376056 47.4325644,9.3761349 47.4325167,9.37619 47.4325653,9.376312 47.4326732,9.3765907 47.4328683,9.3766389 47.4328521,9.3767794 47.4329452,9.3764748 47.4331106,9.3763619 47.4330074))', 4326)
        // geography::STPolyFromText('POLYGON((9.3766833 47.4319973,9.3772045 47.4324131,9.3771257 47.432459,9.3769959 47.4323535,9.3767225 47.4325076,9.3768938 47.432637,9.3769843 47.4325975,9.3772713 47.432826,9.3771862 47.4328789,9.376941 47.4326789,9.3767283 47.4327757,9.3765053 47.4325749,9.376312 47.4326732,9.37619 47.4325653,9.3761349 47.4325167,9.376056 47.4325644,9.3757946 47.43237,9.3760399 47.4322419,9.376144 47.4323272,9.3761809 47.4323125,9.3762975 47.432428,9.3762371 47.4324602,9.3763095 47.4325246,9.3764699 47.4324328,9.3763633 47.4323437,9.3763976 47.4323193,9.3763247 47.4322628,9.3763972 47.4322251,9.3764363 47.4322061,9.3766528 47.4323718,9.3768611 47.4322514,9.3765976 47.4320409,9.3766833 47.4319973))', 4326)

        public static Microsoft.SqlServer.Types.SqlGeography ToPolygon(string text)
        {
            // text = @"POLYGON((-122.358 47.653, -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))";
            System.Data.SqlTypes.SqlChars polygon = new System.Data.SqlTypes.SqlChars(new System.Data.SqlTypes.SqlString(text));
            Microsoft.SqlServer.Types.SqlGeography poly = Microsoft.SqlServer.Types.SqlGeography.STMPolyFromText(polygon, 4326);
            return poly;
        }

        public static Microsoft.SqlServer.Types.SqlGeography ToPolygon(GeoPoint[] points)
        {
            string pointText = ObjJoin(", ", points);
            string text = "POLYGON((" + pointText + "))";

            Microsoft.SqlServer.Types.SqlGeography poly = ToPolygon(text);
            return poly;
        }

        public static void Test()
        {
            // DECLARE @Geom TABLE ( shape geometry ); 
            // SELECT geometry::UnionAggregate(shape).ToString() FROM @Geom;
            // geometry ST_Union(geometry[] g1_array);
            // https://docs.microsoft.com/en-us/sql/t-sql/spatial-geometry/unionaggregate-geometry-data-type?view=sql-server-2017
            // https://gis.stackexchange.com/questions/237644/what-does-the-st-union-of-the-geometry-column-of-two-tables-produce

            GeoPoint[] points = new GeoPoint[]
            {
                  new GeoPoint(0,0)
                , new GeoPoint(0,0)
                , new GeoPoint(0,0)
                , new GeoPoint(0,0)
                , new GeoPoint(0,0)
            };

            string s1 = "POLYGON((9.3763619 47.4330074,9.3764389 47.4329684,9.3764072 47.4329405,9.3763969 47.4329322,9.3759864 47.4326004,9.376056 47.4325644,9.3761349 47.4325167,9.37619 47.4325653,9.376312 47.4326732,9.3765907 47.4328683,9.3766389 47.4328521,9.3767794 47.4329452,9.3764748 47.4331106,9.3763619 47.4330074))";
            string s2 = "POLYGON((9.3766833 47.4319973,9.3772045 47.4324131,9.3771257 47.432459,9.3769959 47.4323535,9.3767225 47.4325076,9.3768938 47.432637,9.3769843 47.4325975,9.3772713 47.432826,9.3771862 47.4328789,9.376941 47.4326789,9.3767283 47.4327757,9.3765053 47.4325749,9.376312 47.4326732,9.37619 47.4325653,9.3761349 47.4325167,9.376056 47.4325644,9.3757946 47.43237,9.3760399 47.4322419,9.376144 47.4323272,9.3761809 47.4323125,9.3762975 47.432428,9.3762371 47.4324602,9.3763095 47.4325246,9.3764699 47.4324328,9.3763633 47.4323437,9.3763976 47.4323193,9.3763247 47.4322628,9.3763972 47.4322251,9.3764363 47.4322061,9.3766528 47.4323718,9.3768611 47.4322514,9.3765976 47.4320409,9.3766833 47.4319973))'";

            // ST_GeomFromText(text WKT, integer srid);
            // ST_Union, ST_AsText
            // ST_GeomFromText('POINT(-2 3)') ) )
            // ST_Intersects( geography geogA , geography geogB )

            // Could not load Microsoft.SqlServer.Server

            Microsoft.SqlServer.Types.SqlGeography poly1 = ToPolygon(s1); // points);
            Microsoft.SqlServer.Types.SqlGeography poly2 = ToPolygon(s2); // points);

            Microsoft.SqlServer.Types.SqlGeography poly3 = poly1.STUnion(poly2);
            System.Data.SqlTypes.SqlChars chars = poly3.STAsText();
            string value = new string(chars.Value);
            System.Console.WriteLine(value);
        }
    }
}
#endif

And while you are at it, the version for the full .NET framwork (core also) should also work if SQL-Server is not installed on the machine that Microsoft.SqlServer.Types is executed on...

stevetalkscode commented 2 years ago

I did a quick proof of concept using @ErikEJ's latest RC package (see https://github.com/ErikEJ/EntityFramework6PowerTools/issues/103) and using the SqlTypes 16 RC in a .NET 6 console app running on WSL and got a DbGeography written to a SQL database which would normally fail (with the could not load DLL error)

dotMorten commented 2 years ago

@stevetalkscode I decompiled the library and I still see a large amount of calls into native code for lots of spatial operations. The serialize/deserialize is all in managed code, so that should just work as you found. Try for instance calling BufferWithCurves on WSL and I'll bet it'll fail.

It they were to target .NET6 instead of netstandard2.1, they could use the SupportedOSPlatformAttribute to flag those set of methods as Windows-only.

ErikEJ commented 2 years ago

@dotMorten But an Improvement on Windows where you got the error in the OP until now.

stevetalkscode commented 2 years ago

@stevetalkscode I decompiled the library and I still see a large amount of calls into native code for lots of spatial operations. The serialize/deserialize is all in managed code, so that should just work as you found. Try for instance calling BufferWithCurves on WSL and I'll bet it'll fail.

It they were to target .NET6 instead of netstandard2.1, they could use the SupportedOSPlatformAttribute to flag those set of methods as Windows-only.

Fair point. I hadn't got as far as testing those bits yet.

David-Engel commented 2 years ago

Are there plans to update System.Data.SqlClient or will this only be supported in Microsoft.Data.SqlClient?

System.Data.SqlClient is in strict maintenance mode. It's not receiving any feature updates.

dotMorten commented 1 year ago

Is this actually complete? Last I checked this only works on x86 and x64 Windows, which isn't in the spirit of claiming .net core support. With .NET 6 running on mac, linux, ios and android, there's not really much benefit to this work until we have a proper crossplatform solution.

The nuget package explorer confirms it too with only runtimes supplied for 2 of the 3 Windows architectures and none for anything else: image

It is in fact rather weird that the library claims to be netstandard2.1 instead of net5.0-windows which is what it really is.

ststeiger commented 1 year ago

It is in fact rather weird that the library claims to be netstandard2.1 instead of net5.0-windows which is what it really is.

Haha, that's not weird, that's expected. Same old Microsoft. By the way, assuming you want polygons that can span accross the equator, it's not finished in x86 and x64, either. (or maybe not, i didn't test since last time i tested)

But I guess quick & dirty was cheapter than proper engineering. Now about filestream support on SQL-Server on Linux ... It's been 6 years since 2017 ... How long did they claim they have to port sql-server, two weeks ? I guess it all depends on what you understand under sql-server.

If you still haven't jumped ship to PostgreSQL/CockroachDB, you have nobody but yourselfs to blame.

pjmlp commented 1 year ago

Supporting only Windows is yet another example of why .NET fails to get some love on UNIXy platforms, marketing message is everything is cross platform in modern .NET, yet when we dive into the details there are hurdles like in this case.

dmytro-gokun commented 1 year ago

@Wraith2 @cheenamalhotra Guys, is there any plans to support SqlGeometry/SqlGeography on Linux? I think lot of folks would like to know if its even planned..

Wraith2 commented 1 year ago

I have no plans to contribute in this area. The sort of response above from @ststeiger illustrates why.

If someone else wanted to work on integrating @dotMorten 's open source geopgraphy stuff instead of relying on the closed source windows-only implementation that the sql server team (note: not the team that works on sql client and you see posting in this repo) maintains, then I'd be happy to help. I just have no need for the api personally and no need to expose myself to harmful toxicity.

ErikEJ commented 1 year ago

@dmytro-gokun Have you tested on Linux and what is not working for you?

dmytro-gokun commented 1 year ago

@ErikEJ Methods like STContains(), STArea() etc

dmytro-gokun commented 1 year ago

@Wraith2 I understand.

I would be happy to help. The problem is that we would need access to that closed source in order to implement it in 100% compatible way. I guess, no one want STContains() which work differently on Windows & Linux. Do you think there is any chance we can get hold of that source code?

ErikEJ commented 1 year ago

Do you think there is any chance we can get hold of that source code?

No

dmytro-gokun commented 1 year ago

@ErikEJ I see. Then i do not think i will be able to help. Reverse engineering C code or trying to guess what exact algorithms were used is not my favorite passtime :)

Does MS at least plan to have this ported to Linux at some point? I guess it would be fair to let community know what to (not) expect.

dotMorten commented 1 year ago

Does MS at least plan to have this ported to Linux at some point?

It is my impression it is in their plans.

dmytro-gokun commented 1 year ago

@dotMorten Would be really nice if someone from MS has actually confirmed or denied this :) @Kaur-Parminder Probably you could help here?

maxmann74 commented 1 year ago

Is this actually complete? Last I checked this only works on x86 and x64 Windows, which isn't in the spirit of claiming .net core support. With .NET 6 running on mac, linux, ios and android, there's not really much benefit to this work until we have a proper crossplatform solution.

The nuget package explorer confirms it too with only runtimes supplied for 2 of the 3 Windows architectures and none for anything else: image

It is in fact rather weird that the library claims to be netstandard2.1 instead of net5.0-windows which is what it really is.

The previous versions of sqlserver.types also had an "msvcr.dll" c++ runtime in these folders. Can anyone confirm if that is no longer needed? Thanks