linq2db.EntityFrameworkCore icon indicating copy to clipboard operation
linq2db.EntityFrameworkCore copied to clipboard

Integration with EFCore.NetTopologySuite

Open MitchellW-DWL opened this issue 1 year ago • 1 comments

I was led to do this by #49 saying to open new if still existing

I've followed all steps in the above adding the following mappings:

Linq2Db Config

        LinqToDBForEFTools.Initialize();
        DataConnection.TurnTraceSwitchOn();
        DataConnection.WriteTraceLine = (message, displayName, traceLevel) =>
        {
            Console.WriteLine($"{displayName} {message}");
        };
        
        var mapping = MappingSchema.Default;
        var geoWriter = new SqlServerBytesWriter();
        mapping.SetConverter<Point, DataParameter>(g =>
        {
            var x = geoWriter.Write(g);
            return new DataParameter("p", x, DataType.Udt);
        });
        
        var reader = new SqlServerBytesReader();
        mapping.SetConverter<byte[], Geometry>((sqlGeomInBytes) => 
        {
            var geometry = reader.Read(sqlGeomInBytes);
            return geometry;
        });

And I have the following code I'm trying to run:

Query

        using (var dc = context.CreateLinqToDBContext())
        {
            await using var table = await dc.CreateTempTableAsync(
                stations, 
                tableName: Guid.NewGuid().ToString(),
                databaseName: "foo", 
                cancellationToken: cancellationToken);
            var targetTable = dc.GetTable<StationEntity>();
            
            var x = await targetTable.Merge()
                .Using(table)
                .On((tgt, src) => tgt.Location.Distance(src.Location) < 10)
                .InsertWhenNotMatched(station => station)
                .UpdateWhenMatched((tgt, src) => src)
                .MergeAsync(cancellationToken);
        }

Note: context here is my injected DbContext instance

But when executing this I get SQL Error

Stack Trace

Exception: Microsoft.Data.SqlClient.SqlException
Message  : 'STDistance' is not a recognized built-in function name.
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlCommand.InternalEndExecuteNonQuery(IAsyncResult asyncResult, Boolean isInternal, String endMethod)
   at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult)
   at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult)
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<InternalExecuteNonQueryAsync>b__210_1(IAsyncResult result)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location ---
   at LinqToDB.Data.DataConnection.ExecuteNonQueryAsync(CancellationToken cancellationToken)
   at LinqToDB.Data.DataConnection.ExecuteNonQueryAsync(CancellationToken cancellationToken)
   at LinqToDB.Data.DataConnection.ExecuteNonQueryDataAsync(CancellationToken cancellationToken)
   at LinqToDB.Data.DataConnection.ExecuteNonQueryDataAsync(CancellationToken cancellationToken)

Which is correct, I can see ON (STDistance([Source].[Location]) < 10) given as the predicate in outputted query _(It should be [Target].[Location].STDistance([Source].[Location]) but using these operations from Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite in other more simpler queries behaves as expected

await context.Stations.Where(station <= station.Distance(somePoint) < 100)
    .ToListAsync()

(I can see that issue #49 was having the exact same with another function at the end of the thread)

MitchellW-DWL avatar Mar 21 '24 19:03 MitchellW-DWL

Just FYI I've managed to get around this for now by defining the following:

    [Sql.Expression("{0}.STDistance({1})", ServerSideOnly = true)]
    public static double DistanceTo(this Point p1, Point p2)
        => throw new NotImplementedException("Method cannot be executed during runtime");

MitchellW-DWL avatar Mar 22 '24 10:03 MitchellW-DWL