2012-12-13 5 views
7

Ecco un documento JSON in cui Mongo fornitore di LINQ fallisce:strano comportamento del fornitore MongoDB LINQ per i campi chiamato "id"

{"results": 
    {"text":"@twitterapi http://tinyurl.com/ctrefg", 
    "to_user_id":396524, 
    "to_user":"TwitterAPI", 
    "from_user":"jkoum", 
    "metadata": 
    { 
     "result_type":"popular", 
     "recent_retweets": 109 
    }, 
    "id":1478555574, 
    "from_user_id":1833773, 
    "iso_language_code":"nl", 
    "source":"<a href=\"http://twitter.com/\">twitter<\/a>", 
    "profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at":"Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id":0, 
    "max_id":1480307926, 
    "refresh_url":"?since_id=1480307926&q=%40twitterapi", 
    "results_per_page":15, 
    "next_page":"?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in":0.031704, 
    "page":1, 
    "query":"%40twitterapi"} 
} 

nota un campo "id". Qui sono correlate C# definizioni di entità:

class Twitter 
{ 
    [BsonId] 
    public ObjectId Id { get; set; } 
    public Result results { get; set; } 
} 

private class Result 
{ 
    public string text { get; set; } 
    public int to_user_id { get; set; } 
    public string to_user { get; set; } 
    public string from_user { get; set; } 
    public Metadata metadata { get; set; } 
    public int id { get; set; } 
    public int from_user_id { get; set; } 
    public string iso_language_code { get; set; } 
    public string source { get; set; } 
    public string profile_image_url { get; set; } 
    public string created_at { get; set; } 
    public int since_id { get; set; } 
    public int max_id { get; set; } 
    public string refresh_url { get; set; } 
    public int results_per_page { get; set; } 
    public string next_page { get; set; } 
    public double completed_in { get; set; } 
    public int page { get; set; } 
    public string query { get; set; } 
} 

class Metadata 
{ 
    public string result_type { get; set; } 
    public int recent_retweets { get; set; } 
} 

se creo una collezione "Twitter" e salvare il documento di cui sopra, poi quando interrogo utilizzando provider di Mongo LINQ, si genera un'eccezione FileFormatException: "Element 'id' non corrisponde ad alcun campo o la proprietà di classe Mongo.Context.Tests.NativeTests + Risultato"

Tuttavia ci sono due soluzioni alternative per risolvere questo problema:

  1. Rinomina Risultato 'campo id', ad esempio, a "idd" sia in JSON doc che nella classe Result. Quindi la query LINQ funziona.
  2. Mantieni il campo "id" ma aggiungi anche un campo "Id" alla classe Result e contrassegnalo con l'attributo [BsonId]. Ora la classe Result contiene entrambi i campi "Id" e "id", ma la query funziona!

Io uso l'API di Mongo per interrogare la raccolta, tutto funziona correttamente, quindi suppongo che questo debba essere un bug in un provider LINQ MongoDB. "id" in un elemento JSON annidato non dovrebbe essere un lavoro riservato, dovrebbe?

UPDATE: Ecco il risultato di un'esecuzione di query API native:

> db.Twitter.find().limit(1); 

{ "_id" : ObjectId("50c9d3a4870f4f17e049332b"), 
"results" : { 
    "text" : "@twitterapi http://tinyurl.com/ctrefg", 
    "to_user_id" : 396524, 
    "to_user" : "TwitterAPI", 
    "from_user" : "jkoum", 
    "metadata" : { "result_type" : "popular", "recent_retweets" : 109 }, 
    "id" : 1478555574, 
    "from_user_id" : 1833773, " 
    iso_language_code" : "nl", 
    "source" : "<a href=\"http://twitter.com/\">twitter</a>", 
    "profile_image_url" : "http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at" : "Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id" : 0, 
    "max_id" : 1480307926, 
    "refresh_url" : "?since_id=1480307926&q=%40twitterapi", "results_per_page" : 15, "next_page" : "?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in" : 0.031704, 
    "page" : 1, 
    "query" : "%40twitterapi" 
    } 
} 
+0

+1 per soluzioni alternative - grazie. È interessante notare che sembra che l'errore venga sollevato solo se un oggetto con istanza singola contiene un campo 'id'. Un elemento figlio 'Elenco' con' id' non ha avuto problemi. – StuartLC

risposta

14

MongoDB richiede che ogni documento memorizzato nel database abbia un campo (a livello di radice) chiamato "_id".

Il driver C# presuppone che qualsiasi campo della classe chiamato "Id", "id" o "_id" debba essere associato al campo "_id" speciale. Questa è una convenzione, che può essere ignorata. Il driver C# non sa che la tua classe Result non deve essere usata come documento root di una collezione, quindi trova il tuo campo "id" e lo mappa su "_id" nel database.

Un modo per sovrascriverlo è cambiare il nome del campo nella classe (come hai scoperto). Quello che puoi poi fare è usare l'attributo [BsonElement] per mappare il tuo nome di campo C# (ad esempio "idd") con qualsiasi nome sia effettivamente usato nel database (ad esempio "id").Per esempio:

public class Result 
{ 
    [BsonElement("id")] 
    public int idd; // matches "id" in the database 
    // other fields 
} 

Un'altra alternativa è quella di sostituire la convenzione che trova il membro "Id" di una classe di sopprimere il comportamento predefinito del conducente # C per la classe di Risultato. Puoi farlo registrando un nuovo profilo Convention per la tua classe Result. Ad esempio:

È necessario eseguire questa operazione molto presto nel programma, prima che la classe del risultato venga mappata.

+0

Ho creato un ticket JIRA per questo problema in quanto sembra che ci dovrebbe essere un modo semplice per ignorare la mappatura di id per _id quando non vuoi che ciò accada. Vedi: https://jira.mongodb.org/browse/CSHARP-648 –

+0

grazie per la precisazione. No, so come funziona internamente. Nel biglietto JIRA mi è piaciuta l'alternativa all'uso dell'attributo BsonNoId per i documenti nidificati. Facile da afferrare e da usare. Se pensiamo alla convenzione sulla configurazione, pensate che la mappatura possa essere disabilitata di default per le classi annidate, cioè se una classe è definita all'interno di un'altra classe e la classe esterna ha un campo ID o [BsonId]? –

0

Il problema è il tuo POCO non corrisponde il tuo JSON

La classe Twitter ha un ID e una classe chiamata risultati
Eppure, il tuo JSON ha solo dei risultati. Ecco dove si trova il problema.

Quindi, in realtà, si sta aggirando la classe Twitter, e solo la creazione di un'istanza di Result

tuo JSON dovrebbe essere simile:

{ 
    _id: ObjectId("50c9c8f3e0ae76405f7d2b5e"), 
    "results": { 
    "text":"@twitterapi http://tinyurl.com/ctrefg", 
    "to_user_id":396524, 
    "to_user":"TwitterAPI", 
    "from_user":"jkoum", 
    "metadata": 
    { 
     "result_type":"popular", 
     "recent_retweets": 109 
    }, 
    "id":1478555574, 
    "from_user_id":1833773, 
    "iso_language_code":"nl", 
    "source":"<a href=\"http://twitter.com/\">twitter<\/a>", 
    "profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at":"Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id":0, 
    "max_id":1480307926, 
    "refresh_url":"?since_id=1480307926&q=%40twitterapi", 
    "results_per_page":15, 
    "next_page":"?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in":0.031704, 
    "page":1, 
    "query":"%40twitterapi"} 
    } 
} 

Modifica

tuo results è in realtà un documento incorporato (nel caso del tuo modello POCO attualmente) quindi dovresti anche segnare Result.ID con [BsonId]

+0

Grazie Alex, per prima cosa ho pensato che avessi spiegato il comportamento, ma poi ho esaminato il contenuto di MongoDB. Dimentica il JSON originale: è stato usato per inserire i dati. Ora i dati sono inseriti e il documento ha il suo ObjectId. Pertanto, quando interrogo la raccolta, l'Id del documento viene mappato correttamente su Twitter.Id e non ci sono più ID Mongo nel documento, quindi l'elemento nidificato non dovrebbe necessariamente mappare nulla. –

+0

Puoi chiarire cosa intendi con "in modo che l'elemento nidificato non debba necessariamente mappare nulla" - potresti pubblicare il risultato letterale di (ad esempio) db.collection.find(). Limit (1) – Alex

+0

Certo, I ' Ho appena aggiornato il post originale con il risultato di una chiamata API Mongo nativa. Come puoi vedere, ObjectId è collegato al documento esterno, non all'elemento "risultati" annidato. Inoltre potrei avere più elementi nidificati e nessuno di essi avrà ID corrispondenti. –