Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jscarle opened this issue May 22, 2024 · 1 comment

Comments

@jscarle
Copy link

jscarle commented May 22, 2024

As a follow up to #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:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using NetTopologySuite;
using NetTopologySuite.Geometries;

internal sealed class GeoCoordinateConverter : ValueConverter<GeoCoordinate, Point>
{
    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<GeoCoordinate>()
            .HaveConversion<GeoCoordinateConverter>()
            .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:

public static double DistanceInMeters(GeoCoordinate coordinate1, GeoCoordinate coordinate2)
    => throw new NotImplementedException();
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:

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants