OData / AspNetCoreOData

ASP.NET Core OData: A server library built upon ODataLib and ASP.NET Core
Other
457 stars 158 forks source link

OData server crashes when trying to update (PATCH) a property of type NTSTopology.Geometry.Point #68

Open fededim opened 3 years ago

fededim commented 3 years ago

I am using Microsoft.AspNetCore.OData 7.5.2 on server and OData Connected Service 0.11.1 on client.

The client code tries to update a property on an Entity with type NTSTopology.Geometry.Point, the client performs the patch call but it crashes on server with this exception

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.MissingMethodException: No parameterless constructor defined for type 'NetTopologySuite.Geometries.Point'.
   at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type)
   at Microsoft.AspNet.OData.Delta`1.Reset(Type structuralType)
   at Microsoft.AspNet.OData.Delta`1..ctor(Type structuralType, IEnumerable`1 updatableProperties, PropertyInfo dynamicDictionaryPropertyInfo)
   at Microsoft.AspNet.OData.Delta`1..ctor(Type structuralType, IEnumerable`1 updatableProperties)
   --- End of inner exception stack trace ---
   at Microsoft.AspNet.OData.Formatter.ODataInputFormatter.<>c__DisplayClass7_0.<ReadRequestBodyAsync>b__1(Exception ex)
   at Microsoft.AspNet.OData.Formatter.ODataInputFormatterHelper.ReadFromStreamAsync(Type type, Object defaultValue, IEdmModel model, Uri baseAddress, IWebApiRequestMessage internalRequest, Func`1 getODataRequestMessage, Func`2 getEdmTypeDeserializer, Func`2 getODataPayloadDeserializer, Func`1 getODataDeserializerContext, Action`1 registerForDisposeAction, Action`1 logErrorAction)
   at Microsoft.AspNet.OData.Formatter.ODataInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)

The client code is this:

            try
            {
                var ctx = new SincroADRODataService.Default.Container(new Uri("http://localhost/SincroADR_Api/odata"));
                var cam = new DataServiceCollection<Camera>(ctx.Cameras.Where(c => c.Id == 41)).SingleOrDefault();
                cam.Location.Y -= 0.2;
                ctx.SaveChanges();
            }
            catch (Exception ex)
            {
                log.LogError(ex, "error");
            }

The server patch method is this:

        [HttpPatch]
        public async Task<IActionResult> Patch([FromODataUri] Int64 key, Delta<Camera> c)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            var entity = await ctx.Set<Camera>().FindAsync(key);
            if (entity == null)
                return NotFound();

            c.Patch(entity);
            await ctx.SaveChangesAsync();
            return Updated(entity);
        }

I have no problem whatsoever in updating other properties. Any idea on how to solve this issue apart from adding the parameterless constructor to NTSTopology.Geometry.Point ?

fededim commented 3 years ago

Just to be more precise, the API action does not "crash", instead returns bad request due to an invalid model state. If you inspect with the debugger the property ModelState.Root.Errors[0].Exception you find the above exception (it's the only one present).

Kasper888 commented 3 years ago

I have replied the error on https://github.com/OData/WebApi/issues/2271#issuecomment-763624013

xuzhg commented 3 years ago

@fededim

I saw the Point class definition here: https://github.com/NetTopologySuite/NetTopologySuite/blob/develop/src/NetTopologySuite/Geometries/Point.cs

This class doesn't have a default constructor and we can't instantize an object of "Point". Please use the POCO (a plain old CLR object, or plain old class object (POCO) ) class as the model type.

For a no-POCO, maybe need a converter. Here's a workaround that you might try it: https://devblogs.microsoft.com/odata/how-to-consume-sql-spatial-data-with-web-api-v2-2-for-odata-v4/

KenitoInc commented 2 years ago

Another blog post that may assist https://devblogs.microsoft.com/odata/customizing-filter-for-spatial-data-in-asp-net-core-odata-8/