dotnet / dotnet/efcore

Add support for value converters to HasDbFunction in order to better support value objects

Open
#33,788 3 comments 3 reactions 1 assignee Assigned to @AndriySvyryd View on GitHub
area-model-building area-query area-type-mapping customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

As a follow up to https://github.com/dotnet/efcore/issues/13752, I'm attempting to use `HasDbFunction` with my `GeoCoordinate` value object to call the database function `STDistance`.

I already have a value converter as defined below:
```csharp
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using NetTopologySuite;
using NetTopologySuite.Geometries;

internal sealed class GeoCoordinateConverter : ValueConverter
{
private static readonly GeometryFactory _geoFactory =
NtsGeometryServices.Instance.CreateGeometryFactory(4326);

public GeoCoordinateConverter()
: base(valueObject => ConvertToValue(valueObject), value => ConvertToGeoCoordinate(value))
{
}

private static Point ConvertToValue(GeoCoordinate geoCoordinate)
{
var coordinate = new Coordinate(geoCoordinate.Longitude.ToDouble(), geoCoordinate.Latitude.ToDouble());
var point = _geoFactory.CreatePoint(coordinate);
return point;
}

private static GeoCoordinate ConvertToGeoCoordinate(Point point)
{
var coordinate = point.Coordinate;
return GeoCoordinate.Convert(coordinate.Y, coordinate.X);
}
}

internal static class GeoCoordinateExtensions
{
public static void ConfigureGeoCoordinate(this ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties()
.HaveConversion()
.HaveColumnType("geography");
}
}
```

The value converter does work when reading and writing entities that contain the `GeoCoordinate` value object. However, when defining the database function as below:

```csharp
public static double DistanceInMeters(GeoCoordinate coordinate1, GeoCoordinate coordinate2)
=> throw new NotImplementedException();
```

```csharp
modelBuilder.HasDbFunction(typeof(MyDbContext).GetMethod(nameof(DistanceInMeters), new[]
{
typeof(GeoCoordinate), typeof(GeoCoordinate)
})!,
b => b.HasTranslation(
e =>
new SqlFunctionExpression(
e.First(),
"STDistance",
false,
false,
typeof(double),
null)));
```

The following exception is thrown when the DbContext is instantiated:
```csharp
System.InvalidOperationException: The parameter 'coordinate1' for the DbFunction
'MyDbContext.DistanceInMeters(GeoCoordinate,GeoCoordinate)' has an invalid type 'GeoCoordinate'.
Ensure the parameter type can be mapped by the current provider.
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.
ValidateDbFunctions(IModel model, IDiagnosticsLogger`1 logger)
```

It seems that `HasDbFunction` does not take value converters into consideration.

Note: This is being developed and tested in .NET 8.0 with EF Core 8.0.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.