Add support for value converters to HasDbFunction in order to better support value objects
- 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
Assessment
This issue has not been assessed yet.