9

lotte lunghe lunghe con questo ...JSON.Net JsonConverter per DbGeography

Fondamentalmente ho un oggetto EF5 Model-First con una proprietà DbGeography. Vorrei applicare un JsonConverter che lo consenta come andata e ritorno come valori semplici di latitudine/longitudine. Sto usando WebAPI.

cercando uscita JSON e l'ingresso in questo modo:

{ 
    "location": 
    { 
     "geopoint": 
     { 
      "latitude":40.770712, 
      "longitude":-73.962011 
     } 
    } 
} 

Qui è la mia definizione di classe e JsonConverter:

[MetadataType(typeof(QueryLocationMetadata))] 
partial class Location 
{ 
} 

public class QueryLocationMetadata 
{ 
    [JsonConverter(typeof(DbGeographyConverter))] 
    public virtual DbGeography GeoPoint { get; set; } 
} 


public class DbGeographyConverter : JsonConverter 
{ 
    private const string LATITUDE_KEY = "latitude"; 
    private const string LONGITUDE_KEY = "longitude"; 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType.Equals(typeof(DbGeography)); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return default(DbGeography); 

     var jObject = JObject.Load(reader); 

     if (!jObject.HasValues || (jObject.Property(LATITUDE_KEY) == null || jObject.Property(LONGITUDE_KEY) == null)) 
      return default(DbGeography); 

     string wkt = string.Format("POINT({1} {0})", jObject[LATITUDE_KEY], jObject[LONGITUDE_KEY]); 
     return DbGeography.FromText(wkt, DbGeography.DefaultCoordinateSystemId); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var dbGeography = value as DbGeography; 

     serializer.Serialize(writer, dbGeography == null || dbGeography.IsEmpty ? null : new { latitude = dbGeography.Latitude.Value, longitude = dbGeography.Longitude.Value }); 
    } 
} 

Quindi, utilizzando questo sono in grado di serializzare e deserializzare con successo un oggetto anche correttamente ma prima che io abbia mai colpito la mia azione ApiController ho il seguente errore:

System.Data.SqlTypes.SqlNullValueException: Data is Null. This method or property cannot be called on Null values. 
    at System.Data.SqlTypes.SqlDouble.get_Value() 
    at GetValueFromSqlDouble(Object) 
    at System.Web.Http.Metadata.Providers.AssociatedMetadataProvider`1.<>c__DisplayClass3.<GetMetadataForPropertiesImpl>b__0() 
    at System.Web.Http.Metadata.ModelMetadata.get_Model() 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateElements(IEnumerable model, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateElements(IEnumerable model, ValidationContext validationContext) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container) 
    at System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, String keyPrefix) 
    at System.Web.Http.ModelBinding.FormatterParameterBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(Object model) 
    at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass36`1.<>c__DisplayClass38.<Then>b__35() 
    at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass49.<ToAsyncVoidTask>b__48() 
    at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken) 

Dopo aver giocato un po 'su e cercando su google tutti i tipi di cose, sono assolutamente in perdita. Capisco che in generale sta tentando di convalidare la proprietà ma un SqlDouble?

risposta

4

Il problema non riguarda il convertitore. Questo è un bug di convalida noto che si verifica quando l'agente di una proprietà pubblica getta nel grafico dell'oggetto. Questo elemento di lavoro tiene traccia del problema:

http://aspnetwebstack.codeplex.com/workitem/740

Nel frattempo, si dovrebbe essere in grado di ottenere intorno ad esso disabilitando convalida:

config.Services.Clear(typeof(ModelValidatorProvider)); 

Ci scusiamo per l'inconveniente.

+0

temporanea o no, almeno che mi permette di mantenere un po 'dei capelli sulla mia testa. Grazie! – Arno

+0

Potrebbe essere necessario utilizzare il nome completo: System.Web.Http.Validation.ModelValidatorProvider come menzionato qui: https://aspnetwebstack.codeplex.com/workitem/55 – Scott

+0

@Youssef, per favore dai un'occhiata al link nel mio risposta. Sono curioso di sapere se questo è un problema noto. – joelmdev

4

Il bug citato da Youssef da allora è stata fissata, ma ho ancora imbattuto in problemi durante la creazione di un custom JsonConverter per DbGeography in che da qualche parte nel processo di convalida del DefaultBodyModelValidator è rimasto bloccato in un ciclo. La mia soluzione non era di disabilitare del tutto la validazione del modello, ma di sostituire il validatore di default con uno derivato che esclude il tipo DbGeography dalla validazione profonda.

Per non ripetermi, you can see the full solution here.

+0

mi hai salvato un altro giorno di rabbia, questo DbGeogrphy è .... davvero il tipo senza valore che abbia mai visto nella mia vita. –

Problemi correlati