Efcore.pg: This method does not support GeometryCollection arguments?

Created on 10 Aug 2018  路  9Comments  路  Source: npgsql/efcore.pg

So we're using Asp.Net Core 2.1.1 + EF Core +npgsql.efcore.pgsql.nettopologysuite for our web api.

One of my model contains a Point property. It's getting serialized correctly, but if I post what I'm getting back to our web api, i'm always getting the exception saying "This method does not support GeometryCollection arguments". It seems like there is a problem in the deserialization, but I have no clue what could be wrong.

Can someone help me with this problem?

System.ArgumentException: This method does not support GeometryCollection arguments at NetTopologySuite.Geometries.Geometry.CheckNotGeometryCollection(IGeometry g) in C:\TeamCity\BuildAgent\work\9f3b4b5334e77626\NetTopologySuite\Geometries\Geometry.cs:line 2057 at NetTopologySuite.Geometries.GeometryCollection.get_Boundary() in C:\TeamCity\BuildAgent\work\9f3b4b5334e77626\NetTopologySuite\Geometries\GeometryCollection.cs:line 233 at Microsoft.Extensions.Internal.PropertyHelper.CallNullSafePropertyGetter[TDeclaringType,TValue](Func`2 getter, Object target) at Microsoft.AspNetCore.Mvc.Internal.DefaultComplexObjectValidationStrategy.Enumerator.GetModel(Object container, ModelMetadata property) at Microsoft.AspNetCore.Mvc.Internal.DefaultComplexObjectValidationStrategy.Enumerator.<>c__DisplayClass10_0.<MoveNext>b__1() at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationEntry.get_Model() at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Validate(ModelMetadata metadata, String key, Object model, Boolean alwaysValidateAtTopLevel) at Microsoft.AspNetCore.Mvc.ModelBinding.ObjectModelValidator.Validate(ActionContext actionContext, ValidationStateDictionary validationState, String prefix, Object model, ModelMetadata metadata) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.EnforceBindRequiredAndValidate(ObjectModelValidator baseObjectValidator, ActionContext actionContext, ParameterDescriptor parameter, ModelMetadata metadata, ModelBindingContext modelBindingContext, ModelBindingResult modelBindingResult) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value) at Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync() at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIIndexMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Most helpful comment

Ok, I got it working. In my Startup.ConfigureServices, I added a bit of code to suppress the Point type validation:

var validator = new SuppressChildValidationMetadataProvider(typeof(Point));
services.AddMvc(options => options.ModelMetadataDetailsProviders.Add(validator));

It seems like the deserialization itself was working correctly, it's the Asp.Net Core validation of the deserialized object that was failing.

All 9 comments

@iamkinetic Can you post some code showing your configuration, such as your DbContext and the code that makes this query?

Sure.

(Very simplified) Model:
```c#
public class Building
{
public Guid Id {get;set;}
public Point Coordinates {get;set;}
}

Mapping:
```c#
public override void Map(EntityTypeBuilder<Building> b)
{
    b.HasKey(m => m.Id);
    b.Property(m => m.Coordinates).HasColumnType("geography");
}

My query is as simple as it get:
c# context.Buildings.First(building => building.id = buildingId);

I'm correctly getting the building with its coordinates from the database and serialization to json also seems to be ok. It's when I post that json back to the controller that I'm getting the exception.

Hmm... Can you post a sample of the post code then (along with some real JSON that triggers the exception)?

Well, I'm getting a 500 from the previously mentionned exception before I even hit the code from the controller.

Here is the json I'm posting:

{
  "coordinates": {
    "coordinateSequence": {
      "dimension": 3,
      "ordinates": 7,
      "count": 1
    },
    "coordinates": [
      {
        "x": -70.672142,
        "y": 46.11679,
        "z": "NaN"
      }
    ],
    "numPoints": 1,
    "isEmpty": false,
    "dimension": 0,
    "boundaryDimension": -1,
    "x": -70.672142,
    "y": 46.11679,
    "coordinate": {
      "x": -70.672142,
      "y": 46.11679,
      "z": "NaN"
    },
    "geometryType": "Point",
    "ogcGeometryType": 1,
    "boundary": [],
    "z": "NaN",
    "m": "NaN",
    "factory": {
      "precisionModel": {
        "isFloating": true,
        "maximumSignificantDigits": 16,
        "scale": 0,
        "precisionModelType": 0,
        "offsetX": 0,
        "offsetY": 0
      },
      "coordinateSequenceFactory": {
        "ordinates": 7
      },
      "srid": -1
    },
    "userData": null,
    "srid": -1,
    "precisionModel": {
      "isFloating": true,
      "maximumSignificantDigits": 16,
      "scale": 0,
      "precisionModelType": 0,
      "offsetX": 0,
      "offsetY": 0
    },
    "numGeometries": 1,
    "isSimple": true,
    "isValid": true,
    "area": 0,
    "length": 0,
    "envelopeInternal": {
      "isNull": false,
      "width": 0,
      "height": 0,
      "minX": -70.672142,
      "maxX": -70.672142,
      "minY": 46.11679,
      "maxY": 46.11679,
      "area": 0,
      "minExtent": 0,
      "maxExtent": 0,
      "centre": {
        "x": -70.672142,
        "y": 46.11679,
        "z": "NaN"
      }
    },
    "isRectangle": false
  },
  "id": "4399f7d5-a01d-4d9d-a6c0-1f1a97301b0f",
}

Well, I'm getting a 500 from the previously mentionned exception before I even hit the code from the controller.

Ah鈥攖his is happening in your binding/validation layer.

I'm not very familiar with NetTopologySuite, but @YohDeadfall may be able to weigh in on what we're generating vs. what your model needs.

We'll probably need to see some controller code and the model your binding against too.

Looking at the exception stack trace, it seems like the Boundary property is being accessed on a collection field - this isn't allowed. This seems to be happening as part of ASP.NET's validation, which appears to be calling all property getters or something similar? I'm not familiar with ASP.NET on this point, you may want to check how to make it ignore certain properties etc.

As a workaround you should be able to copy your values from Point into a simple DTO and use that in ASP.NET.

Either way am closing as non-Npgsql-related, although we can continue the conversation. It would also be great if you can post any solution you find back here for other users.

See also https://github.com/npgsql/npgsql/issues/1981 for other issues regarding direct JSON serialization of NTS spatial types.

Ok, I got it working. In my Startup.ConfigureServices, I added a bit of code to suppress the Point type validation:

var validator = new SuppressChildValidationMetadataProvider(typeof(Point));
services.AddMvc(options => options.ModelMetadataDetailsProviders.Add(validator));

It seems like the deserialization itself was working correctly, it's the Asp.Net Core validation of the deserialized object that was failing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bikeladam picture bikeladam  路  3Comments

sebstr picture sebstr  路  4Comments

aaronhudon picture aaronhudon  路  3Comments

austindrenski picture austindrenski  路  5Comments

roji picture roji  路  4Comments