4

I am using the DbGeography-Type within a SinglePageApplication (with breeze and angular). Now when using the Data with the DbGeography-Type (readOnly) there is no problem.

As soon as I save an entity which has a property of DbGeography-Type I get the following error:

Error getting value from 'WellKnownValue' on 'System.Data.Entity.Spatial.DbGeography'

When the data is serialized to JSON (with newtonsoft JSON.NET or is it ODATA/WebAPI?), the DbGeography gets serialized correctly but the property "WellKownValue" is called "Geography". This is also reflected in the MSDN-Documentation:

https://msdn.microsoft.com/en-us/library/system.data.spatial.dbgeography.wellknownvalue(v=vs.110).aspx

[DataMemberAttribute(Name = "Geography")]
public DbGeographyWellKnownValue WellKnownValue { get; set; } 

My entity looks like this (over the wire):

{
    ..
    "Name" : "Test",
    "Coordinate" : {
        "$id" : "3",
        "$type" : "System.Data.Entity.Spatial.DbGeography, EntityFramework",
        "Geography" : {
            "$id" : "4",
            "$type" : "System.Data.Entity.Spatial.DbGeographyWellKnownValue, EntityFramework",
            "CoordinateSystemId" : 4326,
            "WellKnownText" : "POINT (8.73275400148029 47.5006958431132)"
        }
    }
}

I guess when it is deserialized at a later point, JSON.NET doesn't know that the Geography-Property of my object is acutally called WellKnownValue.

I am using the Newtonsoft.Json-Package version 7.0.1 and Microsoft.Data.OData version 5.6.4.

How can this problem be solved?

NoRyb
  • 1,472
  • 1
  • 14
  • 35
  • So what is `DbGeographyWellKnownValue`? is it important to implement it instead of normal `DbGeography` in EF when the purpose is serializing to json? – Korayem Apr 24 '16 at 00:15
  • 1
    `DbGeography` has a property called `WellKnownValue` which is of type `DbGeographyWellKnownValue`. As soon as `DbGeography` gets serialized to JSON, the property is no longer called `WellKnownValue` but `Geography` in the resulting JSON string (why I don't know). The problem though: for **de**serialization, a class must be able to be instantiated with a default constructor (with no arguments). The DbGeography class has such a constructor but doesn't set some important private properties (see my answer) and so as soon as you try to read the property `WellKnownValue` (getter) you get an error. – NoRyb Apr 25 '16 at 19:35

1 Answers1

6

I found out (with .NET Reflector) that the DbGeography-Type can be instantiated by a default constructor (with no arguments) BUT this default constructor doesn't set some important private members (like _spatialProvider).

This results in a NullReferenceException when WellKnownValue-Getter is called during deserialization. So what I did is what many people before had to do (and I hoped didn't have to do) - I created a custom JsonConverter.

One speciality was, that I had to register it in BreezeWebApiConfig instead of the normal WebApiConfig:

public class MyBreezeConfig : Breeze.ContextProvider.BreezeConfig
{
    protected override JsonSerializerSettings CreateJsonSerializerSettings()
    {
        JsonSerializerSettings result = base.CreateJsonSerializerSettings();
        result.Converters.Add(new WebDbGeographyJsonConverter());
        return result;
    }
}

And the converter:

public class WebDbGeographyJsonConverter : JsonConverter {
    public override bool CanConvert(Type objectType) {
        return typeof(DbGeography).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        var jObj = JObject.Load(reader);
        if (jObj != null) {

            var geography = jObj["Geography"];
            if (geography != null) {
                var wktText = geography["WellKnownText"].Value<string>();
                if (!string.IsNullOrEmpty(wktText)) {
                    var coordSysId = geography["CoordinateSystemId"].Value<int?>() ?? DbGeography.DefaultCoordinateSystemId;
                    return DbGeography.FromText(wktText, coordSysId);
                }
            }
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        throw new NotImplementedException();
    }

    public override bool CanWrite {
        get {
            // use default implementation of Serialization
            return false;
        }
    }
}
NoRyb
  • 1,472
  • 1
  • 14
  • 35
  • What is WebDbGeography? Just a constants holder or something? – Zachary Dow Dec 10 '15 at 21:54
  • Yes it is. I substituted the constants for the strings in my example. I needed these constants in other parts of my code too because I create the breeze metadata by hand. – NoRyb Dec 11 '15 at 07:56
  • Thanks. That's what I assumed they were, but I wanted to be sure. I highly appreciate you making that clear. – Zachary Dow Dec 11 '15 at 13:35