linq2db / linq2db.EntityFrameworkCore

Bring power of Linq To DB to Entity Framework Core projects
MIT License
449 stars 39 forks source link

Integration with EFCore.NetTopologySuite #388

Open MitchellW-DWL opened 4 months ago

MitchellW-DWL commented 4 months ago

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

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

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

Query

```cs 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(); 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.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 commented 4 months ago

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");