Skip to content

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

New issue

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

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

Already on GitHub? # to your account

Open
jscarle opened this issue May 22, 2024 · 3 comments

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.

@jscarle
Copy link
Author

jscarle commented May 23, 2024

Seems related to #28393

@jscarle
Copy link
Author

jscarle commented Jul 6, 2024

I confirmed in a different project that there seems to be absolutely no way to configure a User Defined Function in such a way to make it possible to use parameters that are not a C# primitive type such as int, decimal, or string.

@alandryz83

This comment was marked as off-topic.

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

No branches or pull requests

7 participants